1) basic plots

library(pacman)
p_load(gapminder)
p_load(tidyverse)
summary(gapminder)
        country        continent        year         lifeExp           pop              gdpPercap       
 Afghanistan:  12   Africa  :624   Min.   :1952   Min.   :23.60   Min.   :6.001e+04   Min.   :   241.2  
 Albania    :  12   Americas:300   1st Qu.:1966   1st Qu.:48.20   1st Qu.:2.794e+06   1st Qu.:  1202.1  
 Algeria    :  12   Asia    :396   Median :1980   Median :60.71   Median :7.024e+06   Median :  3531.8  
 Angola     :  12   Europe  :360   Mean   :1980   Mean   :59.47   Mean   :2.960e+07   Mean   :  7215.3  
 Argentina  :  12   Oceania : 24   3rd Qu.:1993   3rd Qu.:70.85   3rd Qu.:1.959e+07   3rd Qu.:  9325.5  
 Australia  :  12                  Max.   :2007   Max.   :82.60   Max.   :1.319e+09   Max.   :113523.1  
 (Other)    :1632                                                                                       

first plots

plot(lifeExp ~ year, gapminder)

plot(lifeExp ~ gdpPercap, gapminder)

plot(lifeExp ~ log(gdpPercap), gapminder)

table(gapminder$continent) # cout how many observation per continent 

  Africa Americas     Asia   Europe  Oceania 
     624      300      396      360       24 
barplot(table(gapminder$continent))

plot(lifeExp ~ year, gapminder, subset = country == "Zimbabwe")
plot(lifeExp ~ log(gdpPercap), gapminder, subset = year == 2007)
subset(gapminder, subset = country == "Cambodia")
subset(gapminder, subset = country %in% c("Japan", "Belgium"), select = c(country, year, lifeExp))

dplyr

filter(gapminder, country == "Rwanda", year > 1979) # filter rows 
gapminder %>% select(year, lifeExp) %>% head(4) # select columns 

my_gap <- gapminder
my_gap %>%  mutate(gdp_billion = pop * gdpPercap/1e+09, 
                   popMil = round(pop/1e+06, 1), 
                   total_years = pop * lifeExp) # create new columns 

my_gap %>% arrange(year, country) # sort by year and coutry

my_gap %>% rename(life_exp = lifeExp, gdp_percap = gdpPercap) # rename fields

my_gap %>% group_by(continent) %>% summarize(n = n(), n_countries = n_distinct(country))
my_gap %>% group_by(continent) %>% summarize(avg_lifeExp = mean(lifeExp))

my_gap %>% 
  select(country, year, continent, lifeExp) %>% 
    group_by(continent, country) %>% 
      mutate(le_delta = lifeExp - lag(lifeExp)) %>% 
        summarize(worst_le_delta = min(le_delta, na.rm = TRUE)) %>% 
          top_n(-1, wt = worst_le_delta) %>%
            arrange(worst_le_delta)

2) other basic plots

library(pacman)
p_load(car)
p_load(ggsci)

prepare data

# random grades 
set.seed(100)
MathGrade <- rnorm(n = 100, mean = 70, sd = 10)
set.seed(1000)
ReadingGrade <- rnorm(n = 100, mean = 65, sd = 13)

# where and how they took the test
TestLocation <- c(rep("Classroom", 50), rep("Home", 50))
TestFormat <- c(rep("Paper", 25), rep("Electronic", 25), rep("Paper", 25), rep("Electronic", 25))
students <- data.frame(MathGrade, ReadingGrade, TestLocation, TestFormat)

# devide different conditions 
PaperTest <- students %>% dplyr::filter(TestFormat == "Paper")
ElectronicTest <- students %>% dplyr::filter(TestFormat == "Electronic")
Classroom <- students %>% dplyr::filter(TestLocation == "Classroom")
Home <- students %>% dplyr::filter(TestLocation == "Home")

# condition with 2 constraints 
PaperTestHome <- students %>% dplyr::filter(TestFormat == "Paper", TestLocation == "Home")
PaperTestClassroom <- students %>% dplyr::filter(TestFormat == "Paper", TestLocation == "Classroom")
ElectronicTestHome <- students %>% dplyr::filter(TestFormat == "Electronic", TestLocation == "Home")
ElectronicTestClassroom <- students %>%  dplyr::filter(TestFormat == "Electronic", TestLocation =="Classroom")

plots

plot(students$MathGrade, students$ReadingGrade, 
     main = "Math grade vs. Reading grade",
     sub = "All conditions", 
     xlab = "Math grade", 
     ylab = "Reading grade",
     xlim = c(40, 100), 
     ylim = c(40, 100), 
     frame.plot = FALSE)

# car one is power up 
car::scatterplot(ReadingGrade ~ MathGrade, 
                 data = students, 
                 smooth = list(degree = 2, style = "none"))

multiple plots with par

main_title <- "Math grade vs. Reading grade"
xlab <- "Math grade"
ylab <- "Reading grade"

op <- par(mfrow = c(2, 2))

#paper test 
plot(PaperTest$MathGrade, PaperTest$ReadingGrade,
     main = main_title,
     sub = "Paper Test", 
     xlab = xlab, ylab = ylab, 
     xlim = c(0, 100), ylim = c(0, 100))

#electronic test 
plot(ElectronicTest$MathGrade, ElectronicTest$ReadingGrade, 
     main = main_title,
     sub = "Electronic Test", 
     xlab = xlab, ylab = ylab, 
     xlim = c(0,100), ylim = c(0, 100))

#classroom test 
plot(Classroom$MathGrade, Classroom$ReadingGrade, 
     main = main_title,
     sub = "Classroom", 
     xlab = xlab, ylab = ylab, 
     xlim = c(0,100), ylim = c(0, 100))

#classroom test 
plot(Home$MathGrade, Home$ReadingGrade, 
     main = main_title,
     sub = "Home", 
     xlab = xlab, ylab = ylab, 
     xlim = c(0,100), ylim = c(0, 100))

par(op) #reset the global paramters 

addons

plot(PaperTest$MathGrade, PaperTest$ReadingGrade,
     main = main_title,
     sub = "Paper Test", 
     xlab = xlab, ylab = 
     ylab, xlim = c(0,100), ylim = c(0, 100))

#add points to an existing plot
points(ElectronicTest$MathGrade, ElectronicTest$ReadingGrade, 
       main = main_title, 
       pch = 2, 
       col = "blue")

# add a legend 
legend("topleft", 
       legend = c("Paper Test", "Electronic Test"),
       col = c("Black", "Blue"), 
       pch = c(1, 2))

scatter plot matrices

my_cols <- c("#00AFBB", "#E7B800", "#FC4E07")

X <- iris %>% dplyr::select(-Species)

pairs(X, pch=19, lower.panel=NULL, cex=0.5, col=my_cols[iris$Species])

boxplots

mm <- as_tibble(morley)

#make factors 
mm$Expt <- factor(mm$Expt)
mm$Run <- factor(mm$Run)
plot(Speed ~ Expt, data = mm, main = "Speed of Light Data", xlab = "Experiment No.")
#without outliers
boxplot(Speed ~ Expt, data = mm, frame = FALSE, outline = FALSE, main = "Michelson Speed of light data", xlab = "Experiment")

strip charts

stripchart(Speed ~ Expt, data = mm, 
           pch = 1:5, col = 1:5, 
           vertical = TRUE,
           method = "jitter",
           main = "Speed by Experiment", xlab = "Experiment")

barplots

# consider only the 1st three rows, to simplify
x <- VADeaths[1:3, "Rural Male"]
# basic bar plot
barplot(x, 
        col = c("#999999", "#E69F00", "#56B4E9"),
        main = "Death rates in Virginia",
        xlab = "Age group", 
        ylab = "Rate",
        horiz = TRUE)

stacked bar plots

# colors 
palette <- ggsci::pal_startrek()
my_cols <- palette(5)
op <- par(mfrow = c(1, 2))


barplot(VADeaths, col = my_cols)
legend("topleft", legend = rownames(VADeaths), fill = my_cols, box.lty = 0, cex = 0.8)

barplot(VADeaths, col = my_cols, beside = TRUE)
legend("topleft", legend = rownames(VADeaths), fill = my_cols, box.lty = 0, cex = 0.8)

line plots

# data generation
x <- seq(1, 10)
y1 <- x * x
y2 <- 2 * y1
op <- par(mfrow = c(1, 2))

# stair steps plot
plot(x, y1, type = "S", xlab = "x", ylab = "y")

# con le palline
plot(x, y1, type = "b", pch = 19, col = "darkorange", xlab = "x", ylab = "y")
lines(x, y2, pch = 18, type = "b", col = "darkred", lty = 2)
legend("topleft", legend = c("x^2", "2x^2"), col = c("blue",
    "darkred"), lty = 1:2, lwd = 2, cex = 0.8)

histogram and density plots

x <- students$MathGrade

hist(x, col = "steelblue", breaks = 20)

dens <- density(x)
plot(dens, col = "blue", main = "Density of Math grades") # a filled version using polygon():
polygon(dens, col = "blue")

QQplots


data(faithful)
x <- as_tibble(faithful) 

lm_fit <- lm(eruptions ~ waiting, data = x) 
summary(lm_fit)

qqnorm(resid(lm_fit), main = "Residuals rankit plot")
qqline(resid(lm_fit))

dot charts

as_tibble(mtcars)
x <- mtcars %>% dplyr::arrange(mpg)
# group by 'cyl' and color groups
grps <- as.factor(x$cyl)
# select the required number of colors from a custom
# palette
my_cols <- (ggsci::pal_futurama())(nlevels(grps))
dotchart(x$mpg, 
         labels = rownames(x), 
         groups = grps, 
         gcolor = my_cols,
         color = my_cols[grps], 
         cex = 0.6, 
         pch = 19, 
         xlab = "mpg")

ggplot 1

preworkout

library(pacman)
p_load(tidyverse)

options(scipen = 999) # turn off scientific notation 
data("midwest", package = "ggplot2")

plotting basic

1) set plotting table and select the data you want

then add pounts aes() is used to tell the graph which part of the dataset we are interested in

par(mfrow = c(2, 2))

g<-ggplot(midwest, aes(x = area, y = poptotal)) +
      geom_point(aes(col=state), size=3)+# add points with a different color for each state 
      geom_smooth(method = "lm") # add an interpolation line 

2) adjust X and Y axis limit

we have 2 option here, the first zooms and consider for regression only the point displayed while the seocnd one only zooms but remember of the outliers

gx <- g + xlim(c(0,0.1)) + ylim(c(0,1000000)) # deletes all the points outiside limits 

g2 <- g + coord_cartesian(xlim = c(0,0.1) , ylim= c(0,1000000) )# only zooms in 

3) change title and labels

g3 <- g2 + labs(title = " Area vs Population", 
          subtitle = "From midwest dataset",
          y= "population",
          x= "Area",
          caption = "midwest demographic")

4) change color palette

g4 <- g3 + scale_color_brewer(palette = "Set3")

5) change X axis texts and ticks

scale_x_continous is for changing the ticks and the text in the axis even in a complex way using functions

g5 <- g4 + scale_x_continuous(breaks = seq(0, 0.1, 0.01), labels = sprintf("%1.2f%%", seq(0, 0.1, 0.01))) + 
           scale_y_continuous(breaks = seq(0,1000000, 200000), labels = function(x){paste0(x/1000, 'K')})
g5
`geom_smooth()` using formula 'y ~ x'

cusotmize look and feel

use themes

gg <- g+scale_x_continuous(breaks = seq(0, 0.1, 0.01))

gg + theme_bw() + labs(subtitle = "BW Theme")
`geom_smooth()` using formula 'y ~ x'

gg + theme_classic()+ labs(subtitle = "classic")
`geom_smooth()` using formula 'y ~ x'

change point color and size

gg<- ggplot(midwest, aes(x = area, y = poptotal)) + # canvas
     geom_point(aes(col = state, size = popdensity))+ # pointswith different color and size
     geom_smooth(method = "loess", se= F)+ # line 
     xlim(c(0, 0.1)) + ylim(c(0,500000))+ # zoom
     labs(title = "Area Vs Population", y= "Population", x = "Area", caption = "midwest")

plot(gg)

customize plot and axis title text

g4 +  theme(plot.title=element_text(size=20, face="bold", family="Roboto", color="tomato",  hjust=0.5, lineheight=1.2),  # title
           plot.subtitle=element_text(size=15,  family="Roboto",face="bold", hjust=0.5),  # 
           plot.caption=element_text(size=15),  # caption
           
           axis.title.x=element_text(vjust=0,  size=15),  # X axis title
           axis.title.y=element_text(size=15),  # Y axis title
           
           axis.text.x=element_text(size=10,  angle = 30, vjust=.5),  # X axis text
           axis.text.y=element_text(size=10))  # Y axis text

modify legend

gg + labs(color = "State", size = "Density") 
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

gg + scale_color_discrete(name = "State") + scale_size_continuous(name = "Density", guide = F) # giude F hide the legend 
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).
Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please use `guide = "none"` instead.

# manually seleect the colours
gg + scale_color_manual(name = "State", 
                        labels = c("Illinois", "Indiana", "Michigan", "Ohio", "winsconsin"),
                values = c(IL = "blue",IN = "red", MI = "green", OH = "brown", WI = "orange"))
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

# change the order of the legends
gg + guides(colour = guide_legend(order = 1), size = guide_legend(order = 2))
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

text and label annotations

midwest_sub <- midwest %>% dplyr::filter(poptotal > 300000) # take only big counties 
midwest_sub$large_county <- ifelse(midwest_sub$poptotal > 300000, midwest_sub$county, "") # create a new field if large 

gg + geom_text(aes(label = large_county), size=2, data= midwest_sub) + # add text only to them 
    theme(legend.position = "none")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).
Warning: Removed 14 rows containing missing values (geom_text).

p_load(ggrepel)

gg + geom_label_repel(aes(label = large_county), size =2, data = midwest_sub) +
     theme(legend.position = "none")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).
Warning: Removed 14 rows containing missing values (geom_label_repel).

some tranformations

gg + coord_flip()
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

gg + scale_x_reverse() + scale_y_reverse()
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
`geom_smooth()` using formula 'y ~ x'

multiple plots

data(mpg, package = "ggplot2")

basic plot

g <- ggplot(mpg, aes(x= displ, y = hwy)) + geom_point() + labs(title = "hwy vs displ") +
     geom_smooth(method = "lm", se = F) + theme_bw()
plot(g)
`geom_smooth()` using formula 'y ~ x'

oi can break this into small plot

g + facet_wrap(~class, nrow = 3) + labs(title = "hwy vs displ")
`geom_smooth()` using formula 'y ~ x'

g + facet_wrap(~class, scales = "free") + labs(title = "hwy vs displ")
`geom_smooth()` using formula 'y ~ x'

g + facet_grid(manufacturer ~ class)
`geom_smooth()` using formula 'y ~ x'

ggplot 2

p_load(tidyverse)

there are 8 categories of plotsthat cover the biggest part of them

Correlation

study how correlated two variables are, usually we use a scatter plot, the geom smooth draws smooting line

theme_set(theme_bw())  # global preset, bw theme
data("midwest", package = "ggplot2")
# midwest <- read.csv('http://goo.gl/G1K41K') # bkup data
# source

# Scatterplot
gg <- ggplot(midwest, aes(x = area, y = poptotal)) + geom_point(aes(col = state,
    size = popdensity)) + geom_smooth(method = "loess", se = F) +
    xlim(c(0, 0.1)) + ylim(c(0, 5e+05)) + labs(subtitle = "Area Vs Population",
    y = "Population", x = "Area", title = "Scatterplot", caption = "Source: midwest")

plot(gg)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

scatterplot with Encircling

do a circle around some points you want to highlight

p_load(ggalt)
also installing the dependencies ‘later’, ‘extrafontdb’, ‘Rttf2pt1’, ‘htmlwidgets’, ‘lazyeval’, ‘crosstalk’, ‘promises’, ‘proj4’, ‘ash’, ‘maps’, ‘extrafont’, ‘plotly’

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/later_1.3.0.tgz'
Content type 'application/x-gzip' length 623693 bytes (609 KB)
==================================================
downloaded 609 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/extrafontdb_1.0.tgz'
Content type 'application/x-gzip' length 6792 bytes
==================================================
downloaded 6792 bytes

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/Rttf2pt1_1.3.10.tgz'
Content type 'application/x-gzip' length 105843 bytes (103 KB)
==================================================
downloaded 103 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/htmlwidgets_1.5.4.tgz'
Content type 'application/x-gzip' length 894885 bytes (873 KB)
==================================================
downloaded 873 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/lazyeval_0.2.2.tgz'
Content type 'application/x-gzip' length 156515 bytes (152 KB)
==================================================
downloaded 152 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/crosstalk_1.2.0.tgz'
Content type 'application/x-gzip' length 406034 bytes (396 KB)
==================================================
downloaded 396 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/promises_1.2.0.1.tgz'
Content type 'application/x-gzip' length 1782018 bytes (1.7 MB)
==================================================
downloaded 1.7 MB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/proj4_1.0-11.tgz'
Content type 'application/x-gzip' length 17103507 bytes (16.3 MB)
==================================================
downloaded 16.3 MB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/ash_1.0-15.tgz'
Content type 'application/x-gzip' length 29954 bytes (29 KB)
==================================================
downloaded 29 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/maps_3.4.0.tgz'
Content type 'application/x-gzip' length 3106040 bytes (3.0 MB)
==================================================
downloaded 3.0 MB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/extrafont_0.18.tgz'
Content type 'application/x-gzip' length 54341 bytes (53 KB)
==================================================
downloaded 53 KB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/plotly_4.10.0.tgz'
Content type 'application/x-gzip' length 3115123 bytes (3.0 MB)
==================================================
downloaded 3.0 MB

trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/ggalt_0.4.0.tgz'
Content type 'application/x-gzip' length 2361598 bytes (2.3 MB)
==================================================
downloaded 2.3 MB

The downloaded binary packages are in
    /var/folders/g9/qnw0ry294vs3gs4801w53bb80000gn/T//RtmpslQMjZ/downloaded_packages

ggalt installed
midwest_select <- midwest %>% dplyr::filter(poptotal > 350000,
                                            poptotal <= 500000,
                                            area > 0.01,
                                            area < 0.1)

# Plot
ggplot(midwest, aes(x=area, y=poptotal)) + 
    geom_point(aes(col=state, size=popdensity)) + # draw points
    geom_smooth(method="loess", se=FALSE) + # draw smoothing line
    xlim(c(0, 0.1)) + 
    ylim(c(0, 500000)) + 
    geom_encircle(aes(x=area, y=poptotal), 
                  data=midwest_select, # filtered dataframe
                  color="red", 
                  size=2, 
                  expand=0.08) + # expand the curve a little bit outside the points
    labs(subtitle="Area Vs Population", 
         y="Population", 
         x="Area", 
         title="Scatterplot + Encircle", 
         caption="Source: midwest")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 15 rows containing non-finite values (stat_smooth).
Warning: Removed 15 rows containing missing values (geom_point).

Jitter plot

whrn the data is integers we may have many overlapping poits, using jitter we can add some random noise to see all the points

data(mpg, package = "ggplot2")  # alternate source: 'http://goo.gl/uEeRGu')
theme_set(theme_bw())
g <- ggplot(mpg, aes(cty, hwy))

g +geom_jitter(width = 0.5, size = 1) + 
      geom_smooth(method = "lm",se = FALSE) + 
      labs(subtitle = "mpg: city vs highway mileage", y = "hwy", x = "cty", title = "Jittered Points")
`geom_smooth()` using formula 'y ~ x'

counts chart

instead of adding noise we can do a bigger point when ther is overlapping

g + geom_count(col = "tomato3", show.legend = TRUE) + 
    labs(subtitle = "mpg: city vs highway mileage", y = "hwy", x = "cty", title = "Counts Plot")

Bubble plot

scatter is for comparing the relationship between two continuos variables while a bubble if you want the relationship whithin the group based on : 1) a categorical value (color) 2) a contonuos variable ( size)

mpg_select <- mpg %>%
    dplyr::filter(manufacturer %in% c("audi", "ford", "honda",
        "hyundai"))

g <- ggplot(mpg_select, aes(displ, cty)) + labs(subtitle = "mpg: City Mileage vs. Displacement",
    title = "Bubble chart")

g + geom_jitter(aes(col = manufacturer, size = hwy)) + geom_smooth(aes(col = manufacturer),
    method = "lm", se = F)
`geom_smooth()` using formula 'y ~ x'

marginal histogram/ boxplot

relationship and distribution in the same graph

p_load(ggExtra)

g <- ggplot(mpg, aes(cty, hwy)) + 
     geom_count(show.legend = FALSE) + # size 
     geom_smooth(method = "lm", se = F) # line 

ggMarginal(g, type = "histogram", fill = "transparent") # add marginal  distribution
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

ggMarginal(g, type = "boxplot", fill = "transparent")
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

ggMarginal(g, type = "density", fill = "transparent")
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

ggMarginal(g, type = "densigram")  # density + histogram
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

correlogram

Correlograms let you examine the correlation of multiple continuous variables present in the same dataframe

p_load(ggcorrplot)

data(mtcars)
dim(mtcars)
[1] 32 11
#> [1] 32 11
# compute the correlation matrix
corr <- round(cor(mtcars), 1)

# plot
ggcorrplot(corr, 
           hc.order = F, # order the corr. matrix by hierarchical clustering
           type = "lower", 
           lab = TRUE, # add corr. coefficients
           lab_size = 3, 
           method="circle", 
           colors = c("tomato2", "white", "springgreen3"), # colors for low, mid, high correlation values
           title="Correlogram of mtcars", 
           ggtheme=theme_bw)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

Deviation

compare variation in values between small number of items

diverging bars

data("mtcars")
# data prep
mtcars <- tibble::rownames_to_column(mtcars, var="car name") %>% # create new column for car names
          mutate(mpg_z=round(scale(mpg), 2), # compute normalized mpg
                 mpg_type=ifelse(mpg_z < 0, "below", "above"),) %>%  # above / below avg flag
          arrange(mpg_z)# sort

mtcars$`car name` <- factor(mtcars$`car name`, levels = mtcars$`car name`)  # convert to factor to retain sorted order in plot.

# diverging bars
ggplot(mtcars, aes(x=`car name`, y=mpg_z, label=mpg_z)) + 
    geom_bar(stat="identity", aes(fill=mpg_type), width=.5)  +
    scale_fill_manual(name="Mileage", 
                      labels = c("Above Average", "Below Average"), 
                      values = c("above"="#00ba38", "below"="#f8766d")) + 
    labs(subtitle="Normalized mileage from mtcars", 
         title= "Diverging Bars") + 
    coord_flip() +
    theme_bw()

diverging lollipop chart

ggplot(mtcars, aes(x = `car name`, y = mpg_z, label = mpg_z)) +
       geom_point(stat = "identity", fill = "black", size = 6) +
       geom_segment(aes(y = 0, x = `car name`, yend = mpg_z, xend = `car name`),color = "blue") + 
       geom_text(color = "white", size = 2) +
       labs(title = "Diverging Lollipop Chart", subtitle = "Normalized mileage from mtcars: Lollipop") +
       ylim(-2.5, 2.5) + coord_flip() + theme_bw()

diverging bot plot

ggplot(mtcars, aes(x = `car name`, y = mpg_z, label = mpg_z)) +
    geom_point(stat = "identity", aes(col = mpg_type), size = 6) +
    scale_color_manual(name = "Mileage", labels = c("Above Average", "Below Average"), values = c(above = "#00ba38", below = "#f8766d")) +
    geom_text(color = "white", size = 2) + 
    labs(title = "Diverging Dot Plot", subtitle = "Normalized mileage from 'mtcars': Dotplot") +
    ylim(-2.5, 2.5) + coord_flip() + theme_bw()

area chart

data("economics", package = "ggplot2")
# Compute %Returns
economics$returns_perc <- c(0, diff(economics$psavert)/economics$psavert[-length(economics$psavert)])


# Create break points and labels for axis ticks
brks <- economics$date[seq(1, length(economics$date), 12)]
lbls <- lubridate::year(brks)

# plot the 1st 100 observations
ggplot(economics[1:100, ], aes(date, returns_perc)) + geom_area() +
    scale_x_date(breaks = brks, labels = lbls) + labs(title = "Area Chart",
    subtitle = "Percentage Returns for Personal Savings", y = "% Returns for Personal savings",
    caption = "Source: economics dataset") + theme_bw() + theme(axis.text.x = element_text(angle = 90))

ranking

A ranking plot is used to compare the position or performance of multiple items with respect to each other. Actual values matter somewhat less than the ranking.

ordered bar chart

# data prep: group mean city mileage by manufacturer.
cty_mpg <- mpg %>% group_by(make = manufacturer) %>% summarise(mileage = mean(cty))
cty_mpg <- arrange(cty_mpg, mileage)  # sort
cty_mpg$make <- factor(cty_mpg$make, levels = cty_mpg$make)  # refactor to retain the order in plot.

# Draw plot
ggplot(cty_mpg, aes(x = make, y = mileage)) + geom_bar(stat = "identity",
    width = 0.5, fill = "tomato3") + labs(title = "Ordered Bar Chart",
    subtitle = "Make Vs Avg. Mileage", caption = "source: mpg") +
    theme_bw() + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6))

lollipop chart

# Draw plot
ggplot(cty_mpg, aes(x = make, y = mileage)) + geom_point(size = 3) +
    geom_segment(aes(x = make, xend = make, y = 0, yend = mileage)) +
    labs(title = "Lollipop Chart", subtitle = "Make Vs Avg. Mileage",
        caption = "source: mpg") + theme_bw() + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6))

dot plot

ggplot(cty_mpg, aes(x=make, y=mileage)) + 
    geom_point(col="tomato2", size=3) + # draw points
    geom_segment(aes(x=make, 
                     xend=make, 
                     y=min(mileage), 
                     yend=max(mileage)), 
                 linetype="dashed", # draw dashed lines
                 size=0.1) +   
    labs(title="Dot Plot", 
         subtitle="Make Vs Avg. Mileage", 
         caption="source: mpg") +  
    coord_flip() +
    theme_classic()

slope chart

library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
# data prep
dataf <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/gdppercap.csv")
colnames(dataf) <- c("continent", "1952", "1957")
# prepare labels
left_label <- paste(dataf$continent, round(dataf$`1952`), sep=", ")
right_label <- paste(dataf$continent, round(dataf$`1957`), sep=", ")
dataf <- dataf %>% mutate(class=ifelse(`1957` - `1952` < 0, "red", "green"))

p <- ggplot(dataf) + geom_segment(aes(x=1, xend=2, y=`1952`, yend=`1957`, col=class), size=.75, show.legend=F) + 
    geom_vline(xintercept=1, linetype="dashed", size=.1) + 
    geom_vline(xintercept=2, linetype="dashed", size=.1) +
    scale_color_manual(labels = c("Up", "Down"), 
                       values = c("green"="#00ba38", "red"="#f8766d")) +  # color of lines
    labs(x="", y="Mean GdpPerCap") +  # Axis labels
    xlim(.5, 2.5) + ylim(0,(1.1*(max(dataf$`1952`, dataf$`1957`)))) +
    theme_classic()

# add texts
p <- p + geom_text(label=left_label, y=dataf$`1952`, x=rep(1, NROW(dataf)), hjust=1.1, size=3.5)
p <- p + geom_text(label=right_label, y=dataf$`1957`, x=rep(2, NROW(dataf)), hjust=-0.1, size=3.5)
p <- p + geom_text(label="Time 1", x=1, y=1.1*(max(dataf$`1952`, dataf$`1957`)), hjust=1.2, size=5)  # title
p <- p + geom_text(label="Time 2", x=2, y=1.1*(max(dataf$`1952`, dataf$`1957`)), hjust=-0.1, size=5)  # title

# Minify theme
p + theme(panel.background = element_blank(), 
          panel.grid = element_blank(),
          axis.ticks = element_blank(),
          axis.text.x = element_blank(),
          panel.border = element_blank(),
          plot.margin = unit(c(1,2,1,2), "cm"))

dumbdell plot

library(ggalt)

health <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/health.csv")
health$Area <- factor(health$Area, levels = as.character(health$Area))  # for the correct ordering of the dumbbells

ggplot(health, aes(x = pct_2014, xend = pct_2013, y = Area, group = Area)) +
    geom_dumbbell(color = "#a3c4dc", size = 0.75, colour_xend = "#0e668b") +
    scale_x_continuous(label = scales::percent) + labs(x = NULL,
    y = NULL, title = "Dumbbell Chart", subtitle = "Pct Change: 2013 vs 2014",
    caption = "Source: https://github.com/hrbrmstr/ggalt") +
    theme_classic() + theme(plot.title = element_text(hjust = 0.5,
    face = "bold"), plot.background = element_rect(fill = "#f7f7f7"),
    panel.background = element_rect(fill = "#f7f7f7"), panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(), panel.grid.major.x = element_line(),
    axis.ticks = element_blank(), legend.position = "top", panel.border = element_blank())

Distribution

histogram

theme_set(theme_classic()) # set the theme beforehand

# histogram on a continuous (numeric) variable
g <- ggplot(mpg, aes(displ)) + scale_fill_brewer(palette = "Spectral")

g + geom_histogram(aes(fill=class), 
                   binwidth = .1, # change binwidth
                   col="black", 
                   size=.1) +  
    labs(title="Histogram with Auto Binning", 
         subtitle="Engine Displacement across Vehicle Classes") 

g + geom_histogram(aes(fill=class), 
                   bins=5, # change number of bins
                   col="black", 
                   size=.1) +
  labs(title="Histogram with Fixed Bins", 
       subtitle="Engine Displacement across Vehicle Classes") 

theme_set(theme_classic())

# Histogram on a Categorical variable
g <- ggplot(mpg, aes(manufacturer))

g + geom_bar(aes(fill = class), width = 0.5) + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6)) + labs(title = "Histogram on Categorical Variable",
    subtitle = "Manufacturer across Vehicle Classes")

density plot

theme_set(theme_classic())

g <- ggplot(mpg, aes(cty))

g + geom_density(aes(fill = factor(cyl)), alpha = 0.8) + labs(title = "Density plot",
    subtitle = "City Mileage Grouped by Number of cylinders",
    caption = "Source: mpg", x = "City Mileage", fill = "# Cylinders")

box plot

theme_set(theme_classic())

g <- ggplot(mpg, aes(class, cty))

g + geom_boxplot(varwidth = TRUE, fill = "plum") + labs(title = "Box plot",
    subtitle = "City Mileage grouped by Class of vehicle", caption = "Source: mpg",
    x = "Class of Vehicle", y = "City Mileage")



g + geom_boxplot(aes(fill = factor(cyl))) + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6)) + labs(title = "Box plot", subtitle = "City Mileage grouped by Class of vehicle",
    caption = "Source: mpg", x = "Class of Vehicle", y = "City Mileage")

dot + box plot

theme_set(theme_bw())

g <- ggplot(mpg, aes(manufacturer, cty))

g + geom_boxplot() + geom_dotplot(binaxis = "y", stackdir = "center",
    dotsize = 0.5, fill = "red") + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6)) + labs(title = "Box plot + Dot plot", subtitle = "City Mileage vs Class: Each dot represents 1 row in source data",
    caption = "Source: mpg", x = "Class of Vehicle", y = "City Mileage")
Bin width defaults to 1/30 of the range of the data. Pick better value with `binwidth`.

g + geom_boxplot(outlier.color = NA) + geom_point(position = position_jitter(width = 0.2),
    size = 1, color = "red") + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6)) + labs(title = "Box plot + Dot plot", subtitle = "City Mileage vs Class: Each dot represents 1 row in source data",
    caption = "Source: mpg", x = "Class of Vehicle", y = "City Mileage")

tufte boxplot

p_load(ggthemes)
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/ggthemes_4.2.4.tgz'
Content type 'application/x-gzip' length 436158 bytes (425 KB)
==================================================
downloaded 425 KB

The downloaded binary packages are in
    /var/folders/g9/qnw0ry294vs3gs4801w53bb80000gn/T//RtmpslQMjZ/downloaded_packages

ggthemes installed
theme_set(theme_tufte())

g <- ggplot(mpg, aes(manufacturer, cty))

g + geom_tufteboxplot() + theme(axis.text.x = element_text(angle = 65,
    vjust = 0.6)) + labs(title = "Tufte Styled Boxplot", subtitle = "City Mileage grouped by Class of vehicle",
    caption = "Source: mpg", x = "Class of Vehicle", y = "City Mileage")

violin plot

theme_set(theme_bw())

g <- ggplot(mpg, aes(class, cty))

g + geom_violin() + labs(title = "Violin plot", subtitle = "City Mileage vs Class of vehicle",
    caption = "Source: mpg", x = "Class of Vehicle", y = "City Mileage")

population piramid

options(scipen = 999)  # turns of scientific notations like 1e+40

# get data
email_campaign_funnel <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv")
# X axis breaks 
brks <- seq(-15000000, 15000000, 5000000)
# X axis labels
lbls <- paste0(as.character(c(seq(15, 0, -5), seq(5, 15, 5))), "m")

# pyramid
ggplot(email_campaign_funnel, aes(x = Stage, y = Users, fill = Gender)) + # Fill column
    geom_bar(stat = "identity", width = .6) +  # draw the bars
    scale_y_continuous(breaks = brks,   # Breaks
                       labels = lbls) + # Labels
    coord_flip() +  # Flip axes
    labs(title="Email Campaign Funnel") +
    theme_tufte() +  # Tufte theme from ggthemes
    theme(plot.title = element_text(hjust = .5), # Center plot title
          axis.ticks = element_blank()) +
    scale_fill_brewer(palette = "Dark2")  # Color palette

Composition

waffle chart

var <- mpg$class  # categorical data 
table(var)  # original category distribution
var
   2seater    compact    midsize    minivan     pickup subcompact        suv 
         5         47         41         11         33         35         62 
#> var
#>    2seater    compact    midsize    minivan     pickup subcompact        suv 
#>          5         47         41         11         33         35         62
# data prep
nrows <- 10  # our waffle chart will be a 10x10 square
dataf <- expand.grid(y = 1:nrows, x = 1:nrows)
categ_table <- round(table(var) * ((nrows * nrows)/(length(var))))  # transform the category distribution so that the counts sum up to 100
categ_table
var
   2seater    compact    midsize    minivan     pickup subcompact        suv 
         2         20         18          5         14         15         26 
#> var
#>    2seater    compact    midsize    minivan     pickup subcompact        suv 
#>          2         20         18          5         14         15         26
# > 2seater compact midsize minivan pickup subcompact suv >
# 2 20 18 5 14 15 26
sum(categ_table)
[1] 100
#> [1] 100

dataf$category <- factor(rep(names(categ_table), categ_table))
# NOTE: if sum(categ_table) is not 100 (i.e. nrows^2), it
# will need adjustment to make the sum to 100.

# waffle chart
ggplot(dataf, aes(x = x, y = y, fill = category)) + geom_tile(color = "black",
    size = 0.5) + scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(expand = c(0,
    0), trans = "reverse") + scale_fill_brewer(palette = "Set3") +
    labs(title = "Waffle Chart", subtitle = "'Class' of vehicles",
        caption = "Source: mpg") + theme(panel.border = element_rect(size = 2),
    plot.title = element_text(size = rel(1.2)), axis.text = element_blank(),
    axis.title = element_blank(), axis.ticks = element_blank(),
    legend.title = element_blank(), legend.position = "right")

pie chart

theme_set(theme_classic())

# Source: Frequency table
dataf <- as.data.frame(table(mpg$class))
colnames(dataf) <- c("class", "freq")

pie <- ggplot(dataf, aes(x = "", y = freq, fill = factor(class))) +
    geom_bar(width = 1, stat = "identity") + theme(axis.line = element_blank(),
    plot.title = element_text(hjust = 0.5)) + labs(fill = "class",
    x = NULL, y = NULL, title = "Pie Chart of class", caption = "Source: mpg")

# what we got so far
pie + coord_polar(theta = "y", start = 0) + theme(axis.ticks = element_blank(),
    axis.text = element_blank(), axis.title = element_blank(),
    panel.grid = element_blank())

treemap

p_load(treemapify)
ggplot(G20, aes(area = gdp_mil_usd, fill = hdi, label = country, subgroup = region)) +
       geom_treemap() + geom_treemap_subgroup_border() +
       geom_treemap_subgroup_text(place = "centre", grow = T, alpha = 0.5,
       colour = "black", fontface = "italic", min.size = 0) +
       geom_treemap_text(colour = "white", place = "topleft", reflow = T)



ggplot(G20, aes(area = 1, label = country, subgroup = hemisphere,
    subgroup2 = region, subgroup3 = econ_classification)) + geom_treemap() +
    geom_treemap_subgroup3_border(colour = "blue", size = 1) +
    geom_treemap_subgroup2_border(colour = "white", size = 3) +
    geom_treemap_subgroup_border(colour = "red", size = 5) +
    geom_treemap_subgroup_text(place = "middle", colour = "red",
        alpha = 0.5, grow = T) + geom_treemap_subgroup2_text(colour = "white",
    alpha = 0.5, fontface = "italic") + geom_treemap_subgroup3_text(place = "top",
    colour = "blue", alpha = 0.5) + geom_treemap_text(colour = "white",
    place = "middle", reflow = T)

bar chart

# data prep: frequency table
freqtable <- table(mpg$manufacturer)
dataf <- as.data.frame.table(freqtable) %>%
    rename(manufacturer = Var1)
theme_set(theme_classic())
g <- ggplot(dataf, aes(manufacturer, Freq))
g + geom_bar(stat = "identity", width = 0.5, fill = "tomato2") +
    labs(title = "Bar Chart", subtitle = "Manufacturer of vehicles",
        caption = "Source: Frequency of Manufacturers from 'mpg' dataset") +
    theme(axis.text.x = element_text(angle = 65, vjust = 0.6))



g <- ggplot(mpg, aes(manufacturer))
g + geom_bar(aes(fill=class), width = 0.5) + # fill by class
    theme(axis.text.x = element_text(angle=65, vjust=0.6)) +
    labs(title="Categorywise Bar Chart", 
         subtitle="Manufacturer of vehicles", 
         caption="Source: Manufacturers from 'mpg' dataset")

Change

from a time serie object

p_load(ggfortify)
p_load(tidyverse)
p_load(zoo)
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/zoo_1.8-10.tgz'
Content type 'application/x-gzip' length 1044220 bytes (1019 KB)
==================================================
downloaded 1019 KB

The downloaded binary packages are in
    /var/folders/g9/qnw0ry294vs3gs4801w53bb80000gn/T//RtmpslQMjZ/downloaded_packages

zoo installed
# load data
data("AirPassengers")
# check they are a ts object
class(AirPassengers)
[1] "ts"
theme_set(theme_classic())

autoplot(AirPassengers) + labs(title = "AirPassengers") + theme(plot.title = element_text(hjust = 0.5))

from a datagframe

data(economics)

# (re)compute %Returns
economics$returns_perc <- c(0, diff(economics$psavert)/economics$psavert[-length(economics$psavert)])

theme_set(theme_classic())

# Allow Default X Axis Labels
ggplot(economics, aes(x = date)) + geom_line(aes(y = returns_perc)) +
    labs(title = "Time Series Chart", subtitle = "Returns Percentage from 'Economics' Dataset",
        caption = "Source: Economics", y = "Returns %")

from a monthly time series

library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
theme_set(theme_bw())

# consider a 24-month timeframe
economics_m <- economics[1:24, ]
# labels and breaks for X axis text
lbls <- paste0(month.abb[month(economics_m$date)], " ", lubridate::year(economics_m$date)) # month.abb is a built-in constant
brks <- economics_m$date

head(brks)
[1] "1967-07-01" "1967-08-01" "1967-09-01" "1967-10-01" "1967-11-01" "1967-12-01"
#> [1] "1967-07-01" "1967-08-01" "1967-09-01" "1967-10-01" "1967-11-01"
#> [6] "1967-12-01"
head(lbls)
[1] "Jul 1967" "Aug 1967" "Sep 1967" "Oct 1967" "Nov 1967" "Dec 1967"
#> [1] "Jul 1967" "Aug 1967" "Sep 1967" "Oct 1967" "Nov 1967" "Dec 1967"

# plot
ggplot(economics_m, aes(x=date)) + 
    geom_line(aes(y=returns_perc)) + 
    labs(title="Monthly Time Series", 
         subtitle="Returns Percentage from Economics Dataset", 
         caption="Source: Economics", 
         y="Returns %") +  # title and caption
    scale_x_date(labels = lbls, 
                 breaks = brks) +  # change to monthly ticks and labels
    theme(axis.text.x = element_text(angle = 90, vjust=0.5),  # rotate x axis text
          panel.grid.minor = element_blank())  

from a yearly time series

theme_set(theme_bw())

# 7.5 years:
economics_y <- economics[1:90, ]

# labels and breaks for X axis text
brks <- economics_y$date[seq(1, length(economics_y$date), 12)] # one break at each year
lbls <- lubridate::year(brks)

# plot
ggplot(economics_y, aes(x=date)) + 
    geom_line(aes(y=returns_perc)) + 
    labs(title="Yearly Time Series", 
         subtitle="Returns Percentage from Economics Dataset", 
         caption="Source: Economics", 
         y="Returns %") +  # title and caption
    scale_x_date(labels = lbls, 
                 breaks = brks) +  # change to monthly ticks and labels
    theme(axis.text.x = element_text(angle = 90, vjust=0.5),  # rotate x axis text
          panel.grid.minor = element_blank())  # turn off minor grid

From Long Data Format: Multiple Time Series in Same Dataframe Column

data(economics_long, package = "ggplot2")
head(economics_long)
theme_set(theme_bw())
# filter & restrict to specific year range
dataf <- economics_long %>% dplyr::filter(variable %in% c("psavert", "uempmed"),
                                          lubridate::year(date) %in% c(1967:1981))

table(dataf$variable)

psavert uempmed 
    174     174 
#> 
#> psavert uempmed 
#>     174     174

# labels and breaks for X axis text
brks <- dataf$date[seq(1, length(dataf$date), 12)] # one break at each year
lbls <- lubridate::year(brks)

# plot
ggplot(dataf, aes(x=date)) + 
    geom_line(aes(y=value, col=variable)) + 
    labs(title="Time Series of Returns Percentage", 
         subtitle="Drawn from Long Data format", 
         caption="Source: Economics", 
         color=NULL) +  # title and caption
    scale_x_date(labels = lbls, breaks = brks) +  # change to monthly ticks and labels
    scale_color_manual(labels = c("psavert", "uempmed"), 
                       values = c("psavert"="#00ba38", "uempmed"="#f8766d")) +  # line color
    theme(axis.text.x = element_text(angle = 90, vjust=0.5, size = 8),  # rotate x axis text
          panel.grid.minor = element_blank())  # turn off minor grid

From Wide Data Format: Data in Multiple Columns of Dataframe

theme_set(theme_bw())

dataf <- economics %>% dplyr::select(date, psavert, uempmed) %>% 
    dplyr::filter(lubridate::year(date) %in% c(1967:1981))
head(dataf)
# labels and breaks for X axis text
brks <- dataf$date[seq(1, length(dataf$date), 12)]
lbls <- lubridate::year(brks)

# plot
ggplot(dataf, aes(x=date)) + 
  geom_line(aes(y=psavert, col="psavert")) + # 1st line
  geom_line(aes(y=uempmed, col="uempmed")) + # 2nd line
  labs(title="Time Series of Returns Percentage", 
       subtitle="Drawn From Wide Data format", 
       caption="Source: Economics", y="value") +  # title and caption
  scale_x_date(labels = lbls, breaks = brks) +  # change to monthly ticks and labels
  scale_color_manual(name="",
                     values = c("psavert"="#00ba38", "uempmed"="#f8766d")) +  # line color
  theme(axis.text.x = element_text(angle = 90, vjust=0.5, size = 8),
        panel.grid.minor = element_blank())  

stacked area chart

theme_set(theme_bw())

dataf <- economics %>% dplyr::select(date, psavert, uempmed) %>% 
    dplyr::filter(lubridate::year(date) %in% c(1967:1981))

# labels and breaks for X axis text
brks <- dataf$date[seq(1, length(dataf$date), 12)]
lbls <- lubridate::year(brks)

# plot
ggplot(dataf, aes(x=date)) + 
    geom_area(aes(y=psavert+uempmed, fill="psavert")) + # 1st "layer"
    geom_area(aes(y=uempmed, fill="uempmed")) + # 2nd "layer" (plotted over the 1st)
    labs(title="Area Chart of Returns Percentage", 
         subtitle="From Wide Data format", 
         caption="Source: Economics", y="value") +  # title and caption
    scale_x_date(labels = lbls, breaks = brks) +  # change to monthly ticks and labels
    scale_fill_manual(name="", 
                      values = c("psavert"="#00ba38", "uempmed"="#f8766d")) +  # line color
    theme(panel.grid.minor = element_blank())  # turn off minor grid

calendar heatmap

library(plyr)
library(scales)
library(zoo)

dataf <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/yahoo.csv")  # Yahoo! stock closing price 2007-2016
dataf$date <- as.Date(dataf$date)  # format date
dataf <- dataf[dataf$year >= 2012, ]  # filter years

# Create Month Week
dataf$yearmonth <- as.yearmon(dataf$date)
dataf$yearmonthf <- factor(dataf$yearmonth)
dataf <- ddply(dataf, .(yearmonthf), transform, monthweek = 1 +
    week - min(week))  # compute week number of month
dataf <- dataf[, c("year", "yearmonthf", "monthf", "week", "monthweek",
    "weekdayf", "VIX.Close")]
head(dataf)
ggplot(dataf, aes(monthweek, weekdayf, fill = VIX.Close)) + geom_tile(colour = "white") +
    facet_grid(year ~ monthf) + scale_fill_gradient(low = "red",
    high = "green") + labs(x = "Week of Month", y = "", title = "Time-Series Calendar Heatmap",
    subtitle = "Yahoo Closing Price", fill = "Close")

slope chart

theme_set(theme_classic())
source_df <- read.csv("https://raw.githubusercontent.com/jkeirstead/r-slopegraph/master/cancer_survival_rates.csv")  # Estimates of % survival rates for different tumors

# Define functions. Source:
# https://github.com/jkeirstead/r-slopegraph Calculates
# slope graph positions based on Edward Tufte's layout
tufte_sort <- function(df, x = "year", y = "value", group = "group",
    min.space = 0.05) {
    ## First rename the columns for consistency
    ids <- match(c(x, y, group), names(df))
    df <- df[, ids]
    names(df) <- c("x", "y", "group")

    ## Expand grid to ensure every combination has a
    ## defined value
    tmp <- expand.grid(x = unique(df$x), group = unique(df$group))
    tmp <- merge(df, tmp, all.y = TRUE)
    df <- mutate(tmp, y = ifelse(is.na(y), 0, y))

    ## Cast into a matrix shape and arrange by first column
    require(reshape2)
    tmp <- dcast(df, group ~ x, value.var = "y")
    ord <- order(tmp[, 2])
    tmp <- tmp[ord, ]

    min.space <- min.space * diff(range(tmp[, -1]))
    yshift <- numeric(nrow(tmp))
    ## Start at 'bottom' row Repeat for rest of the rows
    ## until you hit the top
    for (i in 2:nrow(tmp)) {
        ## Shift subsequent row up by equal space so gap
        ## between two entries is >= minimum
        mat <- as.matrix(tmp[(i - 1):i, -1])
        d.min <- min(diff(mat))
        yshift[i] <- ifelse(d.min < min.space, min.space - d.min,
            0)
    }

    tmp <- cbind(tmp, yshift = cumsum(yshift))

    scale <- 1
    tmp <- melt(tmp, id = c("group", "yshift"), variable.name = "x",
        value.name = "y")
    ## Store these gaps in a separate variable so that they
    ## can be scaled ypos = a*yshift + y

    tmp <- transform(tmp, ypos = y + scale * yshift)
    return(tmp)

}

plot_slopegraph <- function(df) {
    ylabs <- subset(df, x == head(x, 1))$group
    yvals <- subset(df, x == head(x, 1))$ypos
    fontSize <- 3
    gg <- ggplot(df, aes(x = x, y = ypos)) + geom_line(aes(group = group),
        colour = "grey80") + geom_point(colour = "white", size = 8) +
        geom_text(aes(label = y), size = fontSize, family = "American Typewriter") +
        scale_y_continuous(name = "", breaks = yvals, labels = ylabs)
    return(gg)
}

## Prepare data
dataf <- tufte_sort(source_df, x = "year", y = "value", group = "group",
    min.space = 0.05)
Loading required package: reshape2

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
dataf <- transform(dataf, x = factor(x, levels = c(5, 10, 15,
    20), labels = c("5 years", "10 years", "15 years", "20 years")),
    y = round(y))

## Plot
plot_slopegraph(dataf) + labs(title = "Estimates of % survival rates") +
    theme(axis.title = element_blank(), axis.ticks = element_blank(),
        plot.title = element_text(hjust = 0.5, family = "American Typewriter",
            face = "bold"), axis.text = element_text(family = "American Typewriter",
            face = "bold"))

seasonal plot

p_load(forecast)
theme_set(theme_classic())

# Subset data
nottem_small <- window(nottem, start = c(1920, 1), end = c(1925,
    12))  # subset a smaller timewindow

# Plot
ggseasonplot(AirPassengers) + labs(title = "Seasonal plot: International Airline Passengers")

ggseasonplot(nottem_small) + labs(title = "Seasonal plot: Air temperatures at Nottingham Castle")

groups

hierarchical dendrogram

# install.packages(ggdendro)
p_load(ggdendro)
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/ggdendro_0.1.23.tgz'
Content type 'application/x-gzip' length 173221 bytes (169 KB)
==================================================
downloaded 169 KB

The downloaded binary packages are in
    /var/folders/g9/qnw0ry294vs3gs4801w53bb80000gn/T//RtmpslQMjZ/downloaded_packages

ggdendro installed
theme_set(theme_bw())

hc <- hclust(dist(USArrests), method = "average")  # hierarchical clustering

ggdendrogram(hc, rotate = TRUE, size = 2)

Clusters

# load/reload libraries as needed
p_load(ggalt)
p_load(ggfortify)
theme_set(theme_classic())

# we'll use the Iris dataset
# filter out the Species column
dataf <- iris %>% dplyr::select(-Species)
# compute the principal components
pca_mod <- prcomp(dataf)
# convert to dataframe & add back the Species column
df_pc <- data.frame(pca_mod$x, Species=iris$Species)
# create the subsetted dataframes to be encircled in the plot
df_pc_vir <- df_pc %>% dplyr::filter(Species == "virginica") # df for 'virginica'
df_pc_set <- df_pc %>% dplyr::filter(Species == "setosa")  # df for 'setosa'
df_pc_ver <- df_pc %>% dplyr::filter(Species == "versicolor")  # df for 'versicolor'
 
ggplot(df_pc, aes(PC1, PC2, col=Species)) + # base call
    geom_point(aes(shape=Species), size=2) + # add points
    labs(title="Iris Clusters", 
         subtitle="With principal components PC1 and PC2 as X and Y axis",
         caption="Source: Iris") + 
    coord_cartesian(xlim = 1.2 * c(min(df_pc$PC1), max(df_pc$PC1)), 
                    ylim = 1.2 * c(min(df_pc$PC2), max(df_pc$PC2))) + # change axis limits (without deleting points)
    geom_encircle(data = df_pc_vir, aes(x=PC1, y=PC2)) +   # draw circles
    geom_encircle(data = df_pc_set, aes(x=PC1, y=PC2)) + 
    geom_encircle(data = df_pc_ver, aes(x=PC1, y=PC2))

Interactivity

library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following objects are masked from ‘package:plyr’:

    arrange, mutate, rename, summarise

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
data <- data.frame(cond = rep(c("condition_1", "condition_2"),
    each = 10), my_x = 1:100 + rnorm(100, sd = 9), my_y = 1:100 +
    rnorm(100, sd = 16))

my_graph <- ggplot(data, aes(x = my_x, y = my_y)) + geom_point(shape = 1)

# Let's make it interactive using the ggplotly function !
p <- ggplotly(my_graph)
p

animate

INCOMPLETE CHECK LESSON 6 IF YOU WANT

p_load(gapminder)
data <- gapminder

my_graph <- data %>%
    ggplot(aes(x = gdpPercap, y = lifeExp, col = continent, size = pop)) +
    geom_point(alpha = 0.8) + theme_minimal() + theme(legend.position = "bottom") +
    guides(size = "none") + labs(x = "GDP per Capita", y = "Life Expectancy",
    col = "")

p_load(gganimate)

p <- my_graph + transition_time(year)
animate(p, width = 700, height = 450, renderer = ffmpeg_renderer(format = "webm"))

Rendering [>--------------------------------------------------------------------------------------------------------------------------] at 2.6 fps ~ eta: 38s
Rendering [=>-------------------------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 43s
Rendering [===>-----------------------------------------------------------------------------------------------------------------------] at 2.1 fps ~ eta: 46s
Rendering [====>----------------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 44s
Rendering [=====>---------------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 43s
Rendering [======>--------------------------------------------------------------------------------------------------------------------] at 2.1 fps ~ eta: 44s
Rendering [========>------------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 43s
Rendering [=========>-----------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 41s
Rendering [==========>----------------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 40s
Rendering [===========>---------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 41s
Rendering [=============>-------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 40s
Rendering [==============>------------------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 39s
Rendering [===============>-----------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 38s
Rendering [================>----------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 38s
Rendering [=================>---------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 37s
Rendering [===================>-------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 37s
Rendering [====================>------------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 37s
Rendering [=====================>-----------------------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 36s
Rendering [======================>----------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 37s
Rendering [========================>--------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 36s
Rendering [=========================>-------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 36s
Rendering [==========================>------------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 35s
Rendering [===========================>-----------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 35s
Rendering [=============================>---------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 35s
Rendering [==============================>--------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 34s
Rendering [===============================>-------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 33s
Rendering [================================>------------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 33s
Rendering [=================================>-----------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 32s
Rendering [===================================>---------------------------------------------------------------------------------------] at 2.3 fps ~ eta: 31s
Rendering [====================================>--------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 32s
Rendering [=====================================>-------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 31s
Rendering [======================================>------------------------------------------------------------------------------------] at 2.2 fps ~ eta: 30s
Rendering [========================================>----------------------------------------------------------------------------------] at 2.2 fps ~ eta: 30s
Rendering [=========================================>---------------------------------------------------------------------------------] at 2.2 fps ~ eta: 29s
Rendering [==========================================>--------------------------------------------------------------------------------] at 2.2 fps ~ eta: 29s
Rendering [===========================================>-------------------------------------------------------------------------------] at 2.2 fps ~ eta: 29s
Rendering [=============================================>-----------------------------------------------------------------------------] at 2.2 fps ~ eta: 29s
Rendering [==============================================>----------------------------------------------------------------------------] at 2.2 fps ~ eta: 28s
Rendering [===============================================>---------------------------------------------------------------------------] at 2.2 fps ~ eta: 28s
Rendering [================================================>--------------------------------------------------------------------------] at 2.2 fps ~ eta: 27s
Rendering [=================================================>-------------------------------------------------------------------------] at 2.2 fps ~ eta: 27s
Rendering [===================================================>-----------------------------------------------------------------------] at 2.2 fps ~ eta: 26s
Rendering [====================================================>----------------------------------------------------------------------] at 2.2 fps ~ eta: 26s
Rendering [=====================================================>---------------------------------------------------------------------] at 2.2 fps ~ eta: 25s
Rendering [======================================================>--------------------------------------------------------------------] at 2.2 fps ~ eta: 24s
Rendering [========================================================>------------------------------------------------------------------] at 2.3 fps ~ eta: 24s
Rendering [=========================================================>-----------------------------------------------------------------] at 2.3 fps ~ eta: 23s
Rendering [==========================================================>----------------------------------------------------------------] at 2.3 fps ~ eta: 23s
Rendering [===========================================================>---------------------------------------------------------------] at 2.3 fps ~ eta: 22s
Rendering [=============================================================>-------------------------------------------------------------] at 2.3 fps ~ eta: 22s
Rendering [==============================================================>------------------------------------------------------------] at 2.3 fps ~ eta: 21s
Rendering [===============================================================>-----------------------------------------------------------] at 2.3 fps ~ eta: 21s
Rendering [================================================================>----------------------------------------------------------] at 2.3 fps ~ eta: 20s
Rendering [=================================================================>---------------------------------------------------------] at 2.3 fps ~ eta: 20s
Rendering [===================================================================>-------------------------------------------------------] at 2.3 fps ~ eta: 19s
Rendering [====================================================================>------------------------------------------------------] at 2.3 fps ~ eta: 19s
Rendering [=====================================================================>-----------------------------------------------------] at 2.3 fps ~ eta: 19s
Rendering [======================================================================>----------------------------------------------------] at 2.3 fps ~ eta: 18s
Rendering [========================================================================>--------------------------------------------------] at 2.3 fps ~ eta: 18s
Rendering [=========================================================================>-------------------------------------------------] at 2.3 fps ~ eta: 17s
Rendering [==========================================================================>------------------------------------------------] at 2.3 fps ~ eta: 17s
Rendering [===========================================================================>-----------------------------------------------] at 2.3 fps ~ eta: 16s
Rendering [============================================================================>----------------------------------------------] at 2.4 fps ~ eta: 16s
Rendering [==============================================================================>--------------------------------------------] at 2.4 fps ~ eta: 15s
Rendering [===============================================================================>-------------------------------------------] at 2.4 fps ~ eta: 15s
Rendering [================================================================================>------------------------------------------] at 2.4 fps ~ eta: 14s
Rendering [=================================================================================>-----------------------------------------] at 2.4 fps ~ eta: 14s
Rendering [===================================================================================>---------------------------------------] at 2.4 fps ~ eta: 13s
Rendering [====================================================================================>--------------------------------------] at 2.4 fps ~ eta: 13s
Rendering [=====================================================================================>-------------------------------------] at 2.4 fps ~ eta: 13s
Rendering [======================================================================================>------------------------------------] at 2.4 fps ~ eta: 12s
Rendering [========================================================================================>----------------------------------] at 2.4 fps ~ eta: 12s
Rendering [=========================================================================================>---------------------------------] at 2.4 fps ~ eta: 11s
Rendering [==========================================================================================>--------------------------------] at 2.4 fps ~ eta: 11s
Rendering [===========================================================================================>-------------------------------] at 2.4 fps ~ eta: 10s
Rendering [============================================================================================>------------------------------] at 2.4 fps ~ eta: 10s
Rendering [==============================================================================================>----------------------------] at 2.4 fps ~ eta: 10s
Rendering [===============================================================================================>---------------------------] at 2.4 fps ~ eta:  9s
Rendering [================================================================================================>--------------------------] at 2.4 fps ~ eta:  9s
Rendering [=================================================================================================>-------------------------] at 2.4 fps ~ eta:  8s
Rendering [===================================================================================================>-----------------------] at 2.4 fps ~ eta:  8s
Rendering [====================================================================================================>----------------------] at 2.4 fps ~ eta:  7s
Rendering [=====================================================================================================>---------------------] at 2.4 fps ~ eta:  7s
Rendering [======================================================================================================>--------------------] at 2.4 fps ~ eta:  7s
Rendering [========================================================================================================>------------------] at 2.4 fps ~ eta:  6s
Rendering [=========================================================================================================>-----------------] at 2.4 fps ~ eta:  6s
Rendering [==========================================================================================================>----------------] at 2.4 fps ~ eta:  5s
Rendering [===========================================================================================================>---------------] at 2.4 fps ~ eta:  5s
Rendering [============================================================================================================>--------------] at 2.4 fps ~ eta:  5s
Rendering [==============================================================================================================>------------] at 2.4 fps ~ eta:  4s
Rendering [===============================================================================================================>-----------] at 2.4 fps ~ eta:  4s
Rendering [================================================================================================================>----------] at 2.4 fps ~ eta:  3s
Rendering [=================================================================================================================>---------] at 2.4 fps ~ eta:  3s
Rendering [===================================================================================================================>-------] at 2.4 fps ~ eta:  3s
Rendering [====================================================================================================================>------] at 2.4 fps ~ eta:  2s
Rendering [=====================================================================================================================>-----] at 2.4 fps ~ eta:  2s
Rendering [======================================================================================================================>----] at 2.3 fps ~ eta:  1s
Rendering [========================================================================================================================>--] at 2.3 fps ~ eta:  1s
Rendering [=========================================================================================================================>-] at 2.3 fps ~ eta:  0s
Rendering [===========================================================================================================================] at 2.3 fps ~ eta:  0s
                                                                                                                                                             
sh: ffmpeg: command not found
Warning in system2(ffmpeg, c("-pattern_type sequence", paste0("-r ", fps),  :
  error in running command
Error: Rendering with ffmpeg failed
p <- my_graph + transition_time(year) + labs(title = "Year: {frame_time}")

animate(p, width = 700, height = 450, renderer = ffmpeg_renderer(format = "webm"))
p <- my_graph + geom_text(aes(x = min(gdpPercap), y = min(lifeExp),
    label = as.factor(year)), hjust = -2, vjust = -0.2, alpha = 0.2,
    col = "gray", size = 20) + transition_states(as.factor(year),
    state_length = 0)

animate(p, width = 700, height = 450, renderer = ffmpeg_renderer(format = "webm"))

data cleaning

library(tidyverse)
p_load(janitor)

moma <- read_csv("data_artworks.csv", col_types = cols(BeginDate = col_number(),
    EndDate = col_number(), `Length (cm)` = col_number(), `Circumference (cm)` = col_number(),
    `Duration (sec.)` = col_number(), `Diameter (cm)` = col_number())) %>%
    clean_names()
Warning: One or more parsing issues, see `problems()` for details
problems(moma)
library(stringr)
moma <- moma %>%
    mutate(gender = str_replace_all(gender, fixed("(female)",
        ignore_case = TRUE), "F"), gender = str_replace_all(gender,
        fixed("(male)", ignore_case = TRUE), "M"), num_artists = str_count(gender,
        "[:alpha:]"), num_artists = na_if(num_artists, 0), n_female_artists = str_count(gender,
        "F"), n_male_artists = str_count(gender, "M"), artist_gender = case_when(num_artists ==
        1 & n_female_artists == 1 ~ "Female", num_artists ==
        1 & n_male_artists == 1 ~ "Male"))

What different kinds of art classifications are available?

moma %>%
    distinct(classification) %>%
    print(n = Inf)
NA

filter on paintings

library(tidyr)
moma <- moma %>%
    filter(classification == "Painting") %>%
    drop_na(height_cm, width_cm) %>%
    filter(height_cm > 0 & width_cm > 0)

select some columsn

moma <- moma %>%
    select(title, contains("artist"), contains("year"), contains("_cm"),
           classification, department)
write_csv(moma, "artworks-cleaned.csv")

read data

p_load(here)
p_load(readr)
p_load(dplyr)
moma <- read_csv("artworks-cleaned.csv")
Rows: 2253 Columns: 16
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (6): title, artist, artist_bio, artist_gender, classification, department
dbl (6): num_artists, n_female_artists, n_male_artists, depth_cm, height_cm, width_cm
lgl (4): circumference_cm, diameter_cm, length_cm, seat_height_cm

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

some general info like the number

visualize

moma_dim <- moma %>%
    filter(height_cm < 600, width_cm < 760) %>%
    mutate(hw_ratio = height_cm/width_cm, hw_cat = case_when(hw_ratio >
        1 ~ "taller than wide", hw_ratio < 1 ~ "wider than tall",
        hw_ratio == 1 ~ "perfect square"))

library(ggthemes)  # to load the fivethirtyeight theme

ggplot(moma_dim, aes(x = width_cm, y = height_cm, colour = hw_cat)) +
    geom_point(alpha = 0.5) + ggtitle("MoMA Paintings, Tall and Wide") +
    scale_colour_manual(name = "", values = c("gray50", "#FF9900",
        "#B14CF0")) + theme_fivethirtyeight() + theme(axis.title = element_text()) +
    labs(x = "Width", y = "Height")

ggplot(moma_dim, aes(x = width_cm, y = height_cm, colour = hw_cat)) +
    geom_point(alpha = 0.5, show.legend = FALSE) + ggtitle("MoMA Paintings, Tall and Wide") +
    scale_colour_manual(name = "", values = c("gray50", "#ee5863",
        "#6999cd")) + theme_fivethirtyeight() + theme(axis.title = element_text()) +
    labs(x = "Width", y = "Height") + annotate(x = 200, y = 380,
    geom = "text", label = "Taller than\nWide", color = "#ee5863",
    size = 5, hjust = 1, fontface = 2) + annotate(x = 375, y = 100,
    geom = "text", label = "Wider than\nTall", color = "#6999cd",
    size = 5, hjust = 0, fontface = 2)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyAxKSBiYXNpYyBwbG90cwoKYGBge3J9CmxpYnJhcnkocGFjbWFuKQpwX2xvYWQoZ2FwbWluZGVyKQpwX2xvYWQodGlkeXZlcnNlKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGdhcG1pbmRlcikKYGBgCgojIyBmaXJzdCBwbG90cwoKYGBge3J9CnBsb3QobGlmZUV4cCB+IHllYXIsIGdhcG1pbmRlcikKcGxvdChsaWZlRXhwIH4gZ2RwUGVyY2FwLCBnYXBtaW5kZXIpCnBsb3QobGlmZUV4cCB+IGxvZyhnZHBQZXJjYXApLCBnYXBtaW5kZXIpCmBgYAoKYGBge3J9CnRhYmxlKGdhcG1pbmRlciRjb250aW5lbnQpICMgY291dCBob3cgbWFueSBvYnNlcnZhdGlvbiBwZXIgY29udGluZW50IApiYXJwbG90KHRhYmxlKGdhcG1pbmRlciRjb250aW5lbnQpKQpgYGAKCmBgYHtyfQpwbG90KGxpZmVFeHAgfiB5ZWFyLCBnYXBtaW5kZXIsIHN1YnNldCA9IGNvdW50cnkgPT0gIlppbWJhYndlIikKcGxvdChsaWZlRXhwIH4gbG9nKGdkcFBlcmNhcCksIGdhcG1pbmRlciwgc3Vic2V0ID0geWVhciA9PSAyMDA3KQpgYGAKCmBgYHtyfQpzdWJzZXQoZ2FwbWluZGVyLCBzdWJzZXQgPSBjb3VudHJ5ID09ICJDYW1ib2RpYSIpCnN1YnNldChnYXBtaW5kZXIsIHN1YnNldCA9IGNvdW50cnkgJWluJSBjKCJKYXBhbiIsICJCZWxnaXVtIiksIHNlbGVjdCA9IGMoY291bnRyeSwgeWVhciwgbGlmZUV4cCkpCmBgYAoKIyMgZHBseXIKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ID09ICJSd2FuZGEiLCB5ZWFyID4gMTk3OSkgIyBmaWx0ZXIgcm93cyAKZ2FwbWluZGVyICU+JSBzZWxlY3QoeWVhciwgbGlmZUV4cCkgJT4lIGhlYWQoNCkgIyBzZWxlY3QgY29sdW1ucyAKCm15X2dhcCA8LSBnYXBtaW5kZXIKbXlfZ2FwICU+JSAgbXV0YXRlKGdkcF9iaWxsaW9uID0gcG9wICogZ2RwUGVyY2FwLzFlKzA5LCAKICAgICAgICAgICAgICAgICAgIHBvcE1pbCA9IHJvdW5kKHBvcC8xZSswNiwgMSksIAogICAgICAgICAgICAgICAgICAgdG90YWxfeWVhcnMgPSBwb3AgKiBsaWZlRXhwKSAjIGNyZWF0ZSBuZXcgY29sdW1ucyAKCm15X2dhcCAlPiUgYXJyYW5nZSh5ZWFyLCBjb3VudHJ5KSAjIHNvcnQgYnkgeWVhciBhbmQgY291dHJ5CgpteV9nYXAgJT4lIHJlbmFtZShsaWZlX2V4cCA9IGxpZmVFeHAsIGdkcF9wZXJjYXAgPSBnZHBQZXJjYXApICMgcmVuYW1lIGZpZWxkcwoKbXlfZ2FwICU+JSBncm91cF9ieShjb250aW5lbnQpICU+JSBzdW1tYXJpemUobiA9IG4oKSwgbl9jb3VudHJpZXMgPSBuX2Rpc3RpbmN0KGNvdW50cnkpKQpteV9nYXAgJT4lIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIHN1bW1hcml6ZShhdmdfbGlmZUV4cCA9IG1lYW4obGlmZUV4cCkpCgpteV9nYXAgJT4lIAogIHNlbGVjdChjb3VudHJ5LCB5ZWFyLCBjb250aW5lbnQsIGxpZmVFeHApICU+JSAKICAgIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lIAogICAgICBtdXRhdGUobGVfZGVsdGEgPSBsaWZlRXhwIC0gbGFnKGxpZmVFeHApKSAlPiUgCiAgICAgICAgc3VtbWFyaXplKHdvcnN0X2xlX2RlbHRhID0gbWluKGxlX2RlbHRhLCBuYS5ybSA9IFRSVUUpKSAlPiUgCiAgICAgICAgICB0b3BfbigtMSwgd3QgPSB3b3JzdF9sZV9kZWx0YSkgJT4lCiAgICAgICAgICAgIGFycmFuZ2Uod29yc3RfbGVfZGVsdGEpCmBgYAoKIyAyKSBvdGhlciBiYXNpYyBwbG90cwoKYGBge3J9CmxpYnJhcnkocGFjbWFuKQpwX2xvYWQoY2FyKQpwX2xvYWQoZ2dzY2kpCmBgYAoKIyMgcHJlcGFyZSBkYXRhCgpgYGB7cn0KIyByYW5kb20gZ3JhZGVzIApzZXQuc2VlZCgxMDApCk1hdGhHcmFkZSA8LSBybm9ybShuID0gMTAwLCBtZWFuID0gNzAsIHNkID0gMTApCnNldC5zZWVkKDEwMDApClJlYWRpbmdHcmFkZSA8LSBybm9ybShuID0gMTAwLCBtZWFuID0gNjUsIHNkID0gMTMpCgojIHdoZXJlIGFuZCBob3cgdGhleSB0b29rIHRoZSB0ZXN0ClRlc3RMb2NhdGlvbiA8LSBjKHJlcCgiQ2xhc3Nyb29tIiwgNTApLCByZXAoIkhvbWUiLCA1MCkpClRlc3RGb3JtYXQgPC0gYyhyZXAoIlBhcGVyIiwgMjUpLCByZXAoIkVsZWN0cm9uaWMiLCAyNSksIHJlcCgiUGFwZXIiLCAyNSksIHJlcCgiRWxlY3Ryb25pYyIsIDI1KSkKc3R1ZGVudHMgPC0gZGF0YS5mcmFtZShNYXRoR3JhZGUsIFJlYWRpbmdHcmFkZSwgVGVzdExvY2F0aW9uLCBUZXN0Rm9ybWF0KQoKIyBkZXZpZGUgZGlmZmVyZW50IGNvbmRpdGlvbnMgClBhcGVyVGVzdCA8LSBzdHVkZW50cyAlPiUgZHBseXI6OmZpbHRlcihUZXN0Rm9ybWF0ID09ICJQYXBlciIpCkVsZWN0cm9uaWNUZXN0IDwtIHN0dWRlbnRzICU+JSBkcGx5cjo6ZmlsdGVyKFRlc3RGb3JtYXQgPT0gIkVsZWN0cm9uaWMiKQpDbGFzc3Jvb20gPC0gc3R1ZGVudHMgJT4lIGRwbHlyOjpmaWx0ZXIoVGVzdExvY2F0aW9uID09ICJDbGFzc3Jvb20iKQpIb21lIDwtIHN0dWRlbnRzICU+JSBkcGx5cjo6ZmlsdGVyKFRlc3RMb2NhdGlvbiA9PSAiSG9tZSIpCgojIGNvbmRpdGlvbiB3aXRoIDIgY29uc3RyYWludHMgClBhcGVyVGVzdEhvbWUgPC0gc3R1ZGVudHMgJT4lIGRwbHlyOjpmaWx0ZXIoVGVzdEZvcm1hdCA9PSAiUGFwZXIiLCBUZXN0TG9jYXRpb24gPT0gIkhvbWUiKQpQYXBlclRlc3RDbGFzc3Jvb20gPC0gc3R1ZGVudHMgJT4lIGRwbHlyOjpmaWx0ZXIoVGVzdEZvcm1hdCA9PSAiUGFwZXIiLCBUZXN0TG9jYXRpb24gPT0gIkNsYXNzcm9vbSIpCkVsZWN0cm9uaWNUZXN0SG9tZSA8LSBzdHVkZW50cyAlPiUgZHBseXI6OmZpbHRlcihUZXN0Rm9ybWF0ID09ICJFbGVjdHJvbmljIiwgVGVzdExvY2F0aW9uID09ICJIb21lIikKRWxlY3Ryb25pY1Rlc3RDbGFzc3Jvb20gPC0gc3R1ZGVudHMgJT4lICBkcGx5cjo6ZmlsdGVyKFRlc3RGb3JtYXQgPT0gIkVsZWN0cm9uaWMiLCBUZXN0TG9jYXRpb24gPT0iQ2xhc3Nyb29tIikKYGBgCgojIyBwbG90cwoKYGBge3J9CnBsb3Qoc3R1ZGVudHMkTWF0aEdyYWRlLCBzdHVkZW50cyRSZWFkaW5nR3JhZGUsIAogICAgIG1haW4gPSAiTWF0aCBncmFkZSB2cy4gUmVhZGluZyBncmFkZSIsCiAgICAgc3ViID0gIkFsbCBjb25kaXRpb25zIiwgCiAgICAgeGxhYiA9ICJNYXRoIGdyYWRlIiwgCiAgICAgeWxhYiA9ICJSZWFkaW5nIGdyYWRlIiwKICAgICB4bGltID0gYyg0MCwgMTAwKSwgCiAgICAgeWxpbSA9IGMoNDAsIDEwMCksIAogICAgIGZyYW1lLnBsb3QgPSBGQUxTRSkKCiMgY2FyIG9uZSBpcyBwb3dlciB1cCAKY2FyOjpzY2F0dGVycGxvdChSZWFkaW5nR3JhZGUgfiBNYXRoR3JhZGUsIAogICAgICAgICAgICAgICAgIGRhdGEgPSBzdHVkZW50cywgCiAgICAgICAgICAgICAgICAgc21vb3RoID0gbGlzdChkZWdyZWUgPSAyLCBzdHlsZSA9ICJub25lIikpCmBgYAoKbXVsdGlwbGUgcGxvdHMgd2l0aCBwYXIKCmBgYHtyfQptYWluX3RpdGxlIDwtICJNYXRoIGdyYWRlIHZzLiBSZWFkaW5nIGdyYWRlIgp4bGFiIDwtICJNYXRoIGdyYWRlIgp5bGFiIDwtICJSZWFkaW5nIGdyYWRlIgoKb3AgPC0gcGFyKG1mcm93ID0gYygyLCAyKSkKCiNwYXBlciB0ZXN0IApwbG90KFBhcGVyVGVzdCRNYXRoR3JhZGUsIFBhcGVyVGVzdCRSZWFkaW5nR3JhZGUsCiAgICAgbWFpbiA9IG1haW5fdGl0bGUsCiAgICAgc3ViID0gIlBhcGVyIFRlc3QiLCAKICAgICB4bGFiID0geGxhYiwgeWxhYiA9IHlsYWIsIAogICAgIHhsaW0gPSBjKDAsIDEwMCksIHlsaW0gPSBjKDAsIDEwMCkpCgojZWxlY3Ryb25pYyB0ZXN0IApwbG90KEVsZWN0cm9uaWNUZXN0JE1hdGhHcmFkZSwgRWxlY3Ryb25pY1Rlc3QkUmVhZGluZ0dyYWRlLCAKICAgICBtYWluID0gbWFpbl90aXRsZSwKICAgICBzdWIgPSAiRWxlY3Ryb25pYyBUZXN0IiwgCiAgICAgeGxhYiA9IHhsYWIsIHlsYWIgPSB5bGFiLCAKICAgICB4bGltID0gYygwLDEwMCksIHlsaW0gPSBjKDAsIDEwMCkpCgojY2xhc3Nyb29tIHRlc3QgCnBsb3QoQ2xhc3Nyb29tJE1hdGhHcmFkZSwgQ2xhc3Nyb29tJFJlYWRpbmdHcmFkZSwgCiAgICAgbWFpbiA9IG1haW5fdGl0bGUsCiAgICAgc3ViID0gIkNsYXNzcm9vbSIsIAogICAgIHhsYWIgPSB4bGFiLCB5bGFiID0geWxhYiwgCiAgICAgeGxpbSA9IGMoMCwxMDApLCB5bGltID0gYygwLCAxMDApKQoKI2NsYXNzcm9vbSB0ZXN0IApwbG90KEhvbWUkTWF0aEdyYWRlLCBIb21lJFJlYWRpbmdHcmFkZSwgCiAgICAgbWFpbiA9IG1haW5fdGl0bGUsCiAgICAgc3ViID0gIkhvbWUiLCAKICAgICB4bGFiID0geGxhYiwgeWxhYiA9IHlsYWIsIAogICAgIHhsaW0gPSBjKDAsMTAwKSwgeWxpbSA9IGMoMCwgMTAwKSkKCnBhcihvcCkgI3Jlc2V0IHRoZSBnbG9iYWwgcGFyYW10ZXJzIApgYGAKCiMjIGFkZG9ucwoKYGBge3J9CnBsb3QoUGFwZXJUZXN0JE1hdGhHcmFkZSwgUGFwZXJUZXN0JFJlYWRpbmdHcmFkZSwKICAgICBtYWluID0gbWFpbl90aXRsZSwKICAgICBzdWIgPSAiUGFwZXIgVGVzdCIsIAogICAgIHhsYWIgPSB4bGFiLCB5bGFiID0gCiAgICAgeWxhYiwgeGxpbSA9IGMoMCwxMDApLCB5bGltID0gYygwLCAxMDApKQoKI2FkZCBwb2ludHMgdG8gYW4gZXhpc3RpbmcgcGxvdApwb2ludHMoRWxlY3Ryb25pY1Rlc3QkTWF0aEdyYWRlLCBFbGVjdHJvbmljVGVzdCRSZWFkaW5nR3JhZGUsIAogICAgICAgbWFpbiA9IG1haW5fdGl0bGUsIAogICAgICAgcGNoID0gMiwgCiAgICAgICBjb2wgPSAiYmx1ZSIpCgojIGFkZCBhIGxlZ2VuZCAKbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICBsZWdlbmQgPSBjKCJQYXBlciBUZXN0IiwgIkVsZWN0cm9uaWMgVGVzdCIpLAogICAgICAgY29sID0gYygiQmxhY2siLCAiQmx1ZSIpLCAKICAgICAgIHBjaCA9IGMoMSwgMikpCmBgYAoKIyMgc2NhdHRlciBwbG90IG1hdHJpY2VzCgpgYGB7cn0KbXlfY29scyA8LSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpCgpYIDwtIGlyaXMgJT4lIGRwbHlyOjpzZWxlY3QoLVNwZWNpZXMpCgpwYWlycyhYLCBwY2g9MTksIGxvd2VyLnBhbmVsPU5VTEwsIGNleD0wLjUsIGNvbD1teV9jb2xzW2lyaXMkU3BlY2llc10pCmBgYAoKIyMgYm94cGxvdHMKCmBgYHtyfQptbSA8LSBhc190aWJibGUobW9ybGV5KQoKI21ha2UgZmFjdG9ycyAKbW0kRXhwdCA8LSBmYWN0b3IobW0kRXhwdCkKbW0kUnVuIDwtIGZhY3RvcihtbSRSdW4pCmBgYAoKYGBge3J9CnBsb3QoU3BlZWQgfiBFeHB0LCBkYXRhID0gbW0sIG1haW4gPSAiU3BlZWQgb2YgTGlnaHQgRGF0YSIsIHhsYWIgPSAiRXhwZXJpbWVudCBOby4iKQojd2l0aG91dCBvdXRsaWVycwpib3hwbG90KFNwZWVkIH4gRXhwdCwgZGF0YSA9IG1tLCBmcmFtZSA9IEZBTFNFLCBvdXRsaW5lID0gRkFMU0UsIG1haW4gPSAiTWljaGVsc29uIFNwZWVkIG9mIGxpZ2h0IGRhdGEiLCB4bGFiID0gIkV4cGVyaW1lbnQiKQpgYGAKCiMjIHN0cmlwIGNoYXJ0cwoKYGBge3J9CnN0cmlwY2hhcnQoU3BlZWQgfiBFeHB0LCBkYXRhID0gbW0sIAogICAgICAgICAgIHBjaCA9IDE6NSwgY29sID0gMTo1LCAKICAgICAgICAgICB2ZXJ0aWNhbCA9IFRSVUUsCiAgICAgICAgICAgbWV0aG9kID0gImppdHRlciIsCiAgICAgICAgICAgbWFpbiA9ICJTcGVlZCBieSBFeHBlcmltZW50IiwgeGxhYiA9ICJFeHBlcmltZW50IikKYGBgCgojIyBiYXJwbG90cwoKYGBge3J9CiMgY29uc2lkZXIgb25seSB0aGUgMXN0IHRocmVlIHJvd3MsIHRvIHNpbXBsaWZ5CnggPC0gVkFEZWF0aHNbMTozLCAiUnVyYWwgTWFsZSJdCiMgYmFzaWMgYmFyIHBsb3QKYmFycGxvdCh4LCAKICAgICAgICBjb2wgPSBjKCIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIpLAogICAgICAgIG1haW4gPSAiRGVhdGggcmF0ZXMgaW4gVmlyZ2luaWEiLAogICAgICAgIHhsYWIgPSAiQWdlIGdyb3VwIiwgCiAgICAgICAgeWxhYiA9ICJSYXRlIiwKICAgICAgICBob3JpeiA9IFRSVUUpCmBgYAoKc3RhY2tlZCBiYXIgcGxvdHMKCmBgYHtyfQojIGNvbG9ycyAKcGFsZXR0ZSA8LSBnZ3NjaTo6cGFsX3N0YXJ0cmVrKCkKbXlfY29scyA8LSBwYWxldHRlKDUpCm9wIDwtIHBhcihtZnJvdyA9IGMoMSwgMikpCgoKYmFycGxvdChWQURlYXRocywgY29sID0gbXlfY29scykKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gcm93bmFtZXMoVkFEZWF0aHMpLCBmaWxsID0gbXlfY29scywgYm94Lmx0eSA9IDAsIGNleCA9IDAuOCkKCmJhcnBsb3QoVkFEZWF0aHMsIGNvbCA9IG15X2NvbHMsIGJlc2lkZSA9IFRSVUUpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IHJvd25hbWVzKFZBRGVhdGhzKSwgZmlsbCA9IG15X2NvbHMsIGJveC5sdHkgPSAwLCBjZXggPSAwLjgpCmBgYAoKIyMgbGluZSBwbG90cwoKYGBge3J9CiMgZGF0YSBnZW5lcmF0aW9uCnggPC0gc2VxKDEsIDEwKQp5MSA8LSB4ICogeAp5MiA8LSAyICogeTEKb3AgPC0gcGFyKG1mcm93ID0gYygxLCAyKSkKCiMgc3RhaXIgc3RlcHMgcGxvdApwbG90KHgsIHkxLCB0eXBlID0gIlMiLCB4bGFiID0gIngiLCB5bGFiID0gInkiKQoKIyBjb24gbGUgcGFsbGluZQpwbG90KHgsIHkxLCB0eXBlID0gImIiLCBwY2ggPSAxOSwgY29sID0gImRhcmtvcmFuZ2UiLCB4bGFiID0gIngiLCB5bGFiID0gInkiKQpsaW5lcyh4LCB5MiwgcGNoID0gMTgsIHR5cGUgPSAiYiIsIGNvbCA9ICJkYXJrcmVkIiwgbHR5ID0gMikKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gYygieF4yIiwgIjJ4XjIiKSwgY29sID0gYygiYmx1ZSIsCiAgICAiZGFya3JlZCIpLCBsdHkgPSAxOjIsIGx3ZCA9IDIsIGNleCA9IDAuOCkKYGBgCgojIyBoaXN0b2dyYW0gYW5kIGRlbnNpdHkgcGxvdHMKCmBgYHtyfQp4IDwtIHN0dWRlbnRzJE1hdGhHcmFkZQoKaGlzdCh4LCBjb2wgPSAic3RlZWxibHVlIiwgYnJlYWtzID0gMjApCgpkZW5zIDwtIGRlbnNpdHkoeCkKcGxvdChkZW5zLCBjb2wgPSAiYmx1ZSIsIG1haW4gPSAiRGVuc2l0eSBvZiBNYXRoIGdyYWRlcyIpICMgYSBmaWxsZWQgdmVyc2lvbiB1c2luZyBwb2x5Z29uKCk6CnBvbHlnb24oZGVucywgY29sID0gImJsdWUiKQoKYGBgCgojIyBRUXBsb3RzCgpgYGB7cn0KCmRhdGEoZmFpdGhmdWwpCnggPC0gYXNfdGliYmxlKGZhaXRoZnVsKSAKCmxtX2ZpdCA8LSBsbShlcnVwdGlvbnMgfiB3YWl0aW5nLCBkYXRhID0geCkgCnN1bW1hcnkobG1fZml0KQoKcXFub3JtKHJlc2lkKGxtX2ZpdCksIG1haW4gPSAiUmVzaWR1YWxzIHJhbmtpdCBwbG90IikKcXFsaW5lKHJlc2lkKGxtX2ZpdCkpCmBgYAoKIyMgZG90IGNoYXJ0cwoKYGBge3J9CmFzX3RpYmJsZShtdGNhcnMpCnggPC0gbXRjYXJzICU+JSBkcGx5cjo6YXJyYW5nZShtcGcpCmBgYAoKYGBge3J9CiMgZ3JvdXAgYnkgJ2N5bCcgYW5kIGNvbG9yIGdyb3VwcwpncnBzIDwtIGFzLmZhY3Rvcih4JGN5bCkKIyBzZWxlY3QgdGhlIHJlcXVpcmVkIG51bWJlciBvZiBjb2xvcnMgZnJvbSBhIGN1c3RvbQojIHBhbGV0dGUKbXlfY29scyA8LSAoZ2dzY2k6OnBhbF9mdXR1cmFtYSgpKShubGV2ZWxzKGdycHMpKQpkb3RjaGFydCh4JG1wZywgCiAgICAgICAgIGxhYmVscyA9IHJvd25hbWVzKHgpLCAKICAgICAgICAgZ3JvdXBzID0gZ3JwcywgCiAgICAgICAgIGdjb2xvciA9IG15X2NvbHMsCiAgICAgICAgIGNvbG9yID0gbXlfY29sc1tncnBzXSwgCiAgICAgICAgIGNleCA9IDAuNiwgCiAgICAgICAgIHBjaCA9IDE5LCAKICAgICAgICAgeGxhYiA9ICJtcGciKQpgYGAKCiMgZ2dwbG90IDEKCiMjIHByZXdvcmtvdXQKCmBgYHtyfQpsaWJyYXJ5KHBhY21hbikKcF9sb2FkKHRpZHl2ZXJzZSkKCm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjIHR1cm4gb2ZmIHNjaWVudGlmaWMgbm90YXRpb24gCmRhdGEoIm1pZHdlc3QiLCBwYWNrYWdlID0gImdncGxvdDIiKQpgYGAKCiMjIHBsb3R0aW5nIGJhc2ljCgojIyMgMSkgc2V0IHBsb3R0aW5nIHRhYmxlIGFuZCBzZWxlY3QgdGhlIGRhdGEgeW91IHdhbnQKCnRoZW4gYWRkIHBvdW50cyBhZXMoKSBpcyB1c2VkIHRvIHRlbGwgdGhlIGdyYXBoIHdoaWNoIHBhcnQgb2YgdGhlIGRhdGFzZXQgd2UgYXJlIGludGVyZXN0ZWQgaW4KCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKZzwtZ2dwbG90KG1pZHdlc3QsIGFlcyh4ID0gYXJlYSwgeSA9IHBvcHRvdGFsKSkgKwogICAgICBnZW9tX3BvaW50KGFlcyhjb2w9c3RhdGUpLCBzaXplPTMpKyMgYWRkIHBvaW50cyB3aXRoIGEgZGlmZmVyZW50IGNvbG9yIGZvciBlYWNoIHN0YXRlIAogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSAjIGFkZCBhbiBpbnRlcnBvbGF0aW9uIGxpbmUgCgpgYGAKCiMjIyAyKSBhZGp1c3QgWCBhbmQgWSBheGlzIGxpbWl0Cgp3ZSBoYXZlIDIgb3B0aW9uIGhlcmUsIHRoZSBmaXJzdCB6b29tcyBhbmQgY29uc2lkZXIgZm9yIHJlZ3Jlc3Npb24gb25seSB0aGUgcG9pbnQgZGlzcGxheWVkIHdoaWxlIHRoZSBzZW9jbmQgb25lIG9ubHkgem9vbXMgYnV0IHJlbWVtYmVyIG9mIHRoZSBvdXRsaWVycwoKYGBge3J9Cmd4IDwtIGcgKyB4bGltKGMoMCwwLjEpKSArIHlsaW0oYygwLDEwMDAwMDApKSAjIGRlbGV0ZXMgYWxsIHRoZSBwb2ludHMgb3V0aXNpZGUgbGltaXRzIAoKZzIgPC0gZyArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDAuMSkgLCB5bGltPSBjKDAsMTAwMDAwMCkgKSMgb25seSB6b29tcyBpbiAKCgpgYGAKCiMjIyAzKSBjaGFuZ2UgdGl0bGUgYW5kIGxhYmVscwoKYGBge3J9CmczIDwtIGcyICsgbGFicyh0aXRsZSA9ICIgQXJlYSB2cyBQb3B1bGF0aW9uIiwgCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGcm9tIG1pZHdlc3QgZGF0YXNldCIsCiAgICAgICAgICB5PSAicG9wdWxhdGlvbiIsCiAgICAgICAgICB4PSAiQXJlYSIsCiAgICAgICAgICBjYXB0aW9uID0gIm1pZHdlc3QgZGVtb2dyYXBoaWMiKQoKYGBgCgojIyMgNCkgY2hhbmdlIGNvbG9yIHBhbGV0dGUgCmBgYHtyfQpnNCA8LSBnMyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDMiKQpgYGAKCiMjIyA1KSBjaGFuZ2UgWCBheGlzIHRleHRzIGFuZCB0aWNrcwpzY2FsZV94X2NvbnRpbm91cyBpcyBmb3IgY2hhbmdpbmcgdGhlIHRpY2tzIGFuZCB0aGUgdGV4dCBpbiB0aGUgYXhpcyBldmVuIGluIGEgY29tcGxleCB3YXkgdXNpbmcgZnVuY3Rpb25zCmBgYHtyfQpnNSA8LSBnNCArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMC4xLCAwLjAxKSwgbGFiZWxzID0gc3ByaW50ZigiJTEuMmYlJSIsIHNlcSgwLCAwLjEsIDAuMDEpKSkgKyAKICAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMCwgMjAwMDAwKSwgbGFiZWxzID0gZnVuY3Rpb24oeCl7cGFzdGUwKHgvMTAwMCwgJ0snKX0pCmc1CmBgYAoKCiMjIGN1c290bWl6ZSBsb29rIGFuZCBmZWVsIAoKIyMjIHVzZSB0aGVtZXMKCmBgYHtyfQpnZyA8LSBnK3NjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMC4xLCAwLjAxKSkKCmdnICsgdGhlbWVfYncoKSArIGxhYnMoc3VidGl0bGUgPSAiQlcgVGhlbWUiKQpnZyArIHRoZW1lX2NsYXNzaWMoKSsgbGFicyhzdWJ0aXRsZSA9ICJjbGFzc2ljIikKCmBgYAoKIyMjIGNoYW5nZSBwb2ludCBjb2xvciBhbmQgc2l6ZQoKCmBgYHtyfQpnZzwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeCA9IGFyZWEsIHkgPSBwb3B0b3RhbCkpICsgIyBjYW52YXMKICAgICBnZW9tX3BvaW50KGFlcyhjb2wgPSBzdGF0ZSwgc2l6ZSA9IHBvcGRlbnNpdHkpKSsgIyBwb2ludHN3aXRoIGRpZmZlcmVudCBjb2xvciBhbmQgc2l6ZQogICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlPSBGKSsgIyBsaW5lIAogICAgIHhsaW0oYygwLCAwLjEpKSArIHlsaW0oYygwLDUwMDAwMCkpKyAjIHpvb20KICAgICBsYWJzKHRpdGxlID0gIkFyZWEgVnMgUG9wdWxhdGlvbiIsIHk9ICJQb3B1bGF0aW9uIiwgeCA9ICJBcmVhIiwgY2FwdGlvbiA9ICJtaWR3ZXN0IikKCnBsb3QoZ2cpCmBgYAoKCiMjIyBjdXN0b21pemUgcGxvdCBhbmQgYXhpcyB0aXRsZSB0ZXh0CgpgYGB7cn0KZzQgKyAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0yMCwgZmFjZT0iYm9sZCIsIGZhbWlseT0iUm9ib3RvIiwgY29sb3I9InRvbWF0byIsICBoanVzdD0wLjUsIGxpbmVoZWlnaHQ9MS4yKSwgICMgdGl0bGUKICAgICAgICAgICBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE1LCAgZmFtaWx5PSJSb2JvdG8iLGZhY2U9ImJvbGQiLCBoanVzdD0wLjUpLCAgIyAKICAgICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KHNpemU9MTUpLCAgIyBjYXB0aW9uCiAgICAgICAgICAgCiAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dCh2anVzdD0wLCAgc2l6ZT0xNSksICAjIFggYXhpcyB0aXRsZQogICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQoc2l6ZT0xNSksICAjIFkgYXhpcyB0aXRsZQogICAgICAgICAgIAogICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEwLCAgYW5nbGUgPSAzMCwgdmp1c3Q9LjUpLCAgIyBYIGF4aXMgdGV4dAogICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTEwKSkgICMgWSBheGlzIHRleHQKYGBgCgojIyMgbW9kaWZ5IGxlZ2VuZCAKCmBgYHtyfQpnZyArIGxhYnMoY29sb3IgPSAiU3RhdGUiLCBzaXplID0gIkRlbnNpdHkiKSAKZ2cgKyBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIlN0YXRlIikgKyBzY2FsZV9zaXplX2NvbnRpbnVvdXMobmFtZSA9ICJEZW5zaXR5IiwgZ3VpZGUgPSBGKSAjIGdpdWRlIEYgaGlkZSB0aGUgbGVnZW5kIAoKIyBtYW51YWxseSBzZWxlZWN0IHRoZSBjb2xvdXJzCmdnICsgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiU3RhdGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiSWxsaW5vaXMiLCAiSW5kaWFuYSIsICJNaWNoaWdhbiIsICJPaGlvIiwgIndpbnNjb25zaW4iKSwKICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoSUwgPSAiYmx1ZSIsSU4gPSAicmVkIiwgTUkgPSAiZ3JlZW4iLCBPSCA9ICJicm93biIsIFdJID0gIm9yYW5nZSIpKQoKIyBjaGFuZ2UgdGhlIG9yZGVyIG9mIHRoZSBsZWdlbmRzCmdnICsgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBzaXplID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpCmBgYAojIyMgdGV4dCBhbmQgbGFiZWwgYW5ub3RhdGlvbnMKCgpgYGB7cn0KbWlkd2VzdF9zdWIgPC0gbWlkd2VzdCAlPiUgZHBseXI6OmZpbHRlcihwb3B0b3RhbCA+IDMwMDAwMCkgIyB0YWtlIG9ubHkgYmlnIGNvdW50aWVzIAptaWR3ZXN0X3N1YiRsYXJnZV9jb3VudHkgPC0gaWZlbHNlKG1pZHdlc3Rfc3ViJHBvcHRvdGFsID4gMzAwMDAwLCBtaWR3ZXN0X3N1YiRjb3VudHksICIiKSAjIGNyZWF0ZSBhIG5ldyBmaWVsZCBpZiBsYXJnZSAKCmdnICsgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhcmdlX2NvdW50eSksIHNpemU9MiwgZGF0YT0gbWlkd2VzdF9zdWIpICsgIyBhZGQgdGV4dCBvbmx5IHRvIHRoZW0gCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIG1vcmUgcHJldHR5IHRleHQgCnBfbG9hZChnZ3JlcGVsKQoKZ2cgKyBnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IGxhcmdlX2NvdW50eSksIHNpemUgPTIsIGRhdGEgPSBtaWR3ZXN0X3N1YikgKwogICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCiMjIyBzb21lIHRyYW5mb3JtYXRpb25zIApgYGB7cn0KZ2cgKyBjb29yZF9mbGlwKCkKZ2cgKyBzY2FsZV94X3JldmVyc2UoKSArIHNjYWxlX3lfcmV2ZXJzZSgpCmBgYAojIyBtdWx0aXBsZSBwbG90cyAKCmBgYHtyfQpkYXRhKG1wZywgcGFja2FnZSA9ICJnZ3Bsb3QyIikKYGBgCgpiYXNpYyBwbG90IApgYGB7cn0KZyA8LSBnZ3Bsb3QobXBnLCBhZXMoeD0gZGlzcGwsIHkgPSBod3kpKSArIGdlb21fcG9pbnQoKSArIGxhYnModGl0bGUgPSAiaHd5IHZzIGRpc3BsIikgKwogICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikgKyB0aGVtZV9idygpCnBsb3QoZykKYGBgCm9pIGNhbiBicmVhayB0aGlzIGludG8gc21hbGwgcGxvdCAKCmBgYHtyfQpnICsgZmFjZXRfd3JhcCh+Y2xhc3MsIG5yb3cgPSAzKSArIGxhYnModGl0bGUgPSAiaHd5IHZzIGRpc3BsIikKZyArIGZhY2V0X3dyYXAofmNsYXNzLCBzY2FsZXMgPSAiZnJlZSIpICsgbGFicyh0aXRsZSA9ICJod3kgdnMgZGlzcGwiKQpnICsgZmFjZXRfZ3JpZChtYW51ZmFjdHVyZXIgfiBjbGFzcykKYGBgCgojIGdncGxvdCAyCmBgYHtyfQpwX2xvYWQodGlkeXZlcnNlKQpgYGAKCnRoZXJlIGFyZSA4IGNhdGVnb3JpZXMgb2YgcGxvdHN0aGF0IGNvdmVyIHRoZSBiaWdnZXN0IHBhcnQgb2YgdGhlbSAKCiMjIENvcnJlbGF0aW9uIApzdHVkeSBob3cgY29ycmVsYXRlZCB0d28gdmFyaWFibGVzIGFyZSwgdXN1YWxseSB3ZSB1c2UgYSBzY2F0dGVyIHBsb3QsIHRoZSBnZW9tIHNtb290aCBkcmF3cyBzbW9vdGluZyBsaW5lIAoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9idygpKSAgIyBnbG9iYWwgcHJlc2V0LCBidyB0aGVtZQpkYXRhKCJtaWR3ZXN0IiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikKIyBtaWR3ZXN0IDwtIHJlYWQuY3N2KCdodHRwOi8vZ29vLmdsL0cxSzQxSycpICMgYmt1cCBkYXRhCiMgc291cmNlCgojIFNjYXR0ZXJwbG90CmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeCA9IGFyZWEsIHkgPSBwb3B0b3RhbCkpICsgZ2VvbV9wb2ludChhZXMoY29sID0gc3RhdGUsCiAgICBzaXplID0gcG9wZGVuc2l0eSkpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGKSArCiAgICB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNWUrMDUpKSArIGxhYnMoc3VidGl0bGUgPSAiQXJlYSBWcyBQb3B1bGF0aW9uIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsIHggPSAiQXJlYSIsIHRpdGxlID0gIlNjYXR0ZXJwbG90IiwgY2FwdGlvbiA9ICJTb3VyY2U6IG1pZHdlc3QiKQoKcGxvdChnZykKCmBgYAojIyMgc2NhdHRlcnBsb3Qgd2l0aCBFbmNpcmNsaW5nIApkbyBhIGNpcmNsZSBhcm91bmQgc29tZSBwb2ludHMgeW91IHdhbnQgdG8gaGlnaGxpZ2h0CgpgYGB7cn0KcF9sb2FkKGdnYWx0KQojIHNlbGVjdCBhIHN1YnNldCBvZiBjb3VudHkgZmlsdGVyaW5nIAptaWR3ZXN0X3NlbGVjdCA8LSBtaWR3ZXN0ICU+JSBkcGx5cjo6ZmlsdGVyKHBvcHRvdGFsID4gMzUwMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHRvdGFsIDw9IDUwMDAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmVhID4gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmVhIDwgMC4xKQoKIyBQbG90CmdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyAKICAgIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAjIGRyYXcgcG9pbnRzCiAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RkFMU0UpICsgIyBkcmF3IHNtb290aGluZyBsaW5lCiAgICB4bGltKGMoMCwgMC4xKSkgKyAKICAgIHlsaW0oYygwLCA1MDAwMDApKSArIAogICAgZ2VvbV9lbmNpcmNsZShhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSwgCiAgICAgICAgICAgICAgICAgIGRhdGE9bWlkd2VzdF9zZWxlY3QsICMgZmlsdGVyZWQgZGF0YWZyYW1lCiAgICAgICAgICAgICAgICAgIGNvbG9yPSJyZWQiLCAKICAgICAgICAgICAgICAgICAgc2l6ZT0yLCAKICAgICAgICAgICAgICAgICAgZXhwYW5kPTAuMDgpICsgIyBleHBhbmQgdGhlIGN1cnZlIGEgbGl0dGxlIGJpdCBvdXRzaWRlIHRoZSBwb2ludHMKICAgIGxhYnMoc3VidGl0bGU9IkFyZWEgVnMgUG9wdWxhdGlvbiIsIAogICAgICAgICB5PSJQb3B1bGF0aW9uIiwgCiAgICAgICAgIHg9IkFyZWEiLCAKICAgICAgICAgdGl0bGU9IlNjYXR0ZXJwbG90ICsgRW5jaXJjbGUiLCAKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBtaWR3ZXN0IikKYGBgCiMjIyBKaXR0ZXIgcGxvdCAKd2hybiB0aGUgZGF0YSBpcyBpbnRlZ2VycyB3ZSBtYXkgaGF2ZSBtYW55IG92ZXJsYXBwaW5nIHBvaXRzLCB1c2luZyBqaXR0ZXIgd2UgY2FuIGFkZCBzb21lIHJhbmRvbSBub2lzZSB0byBzZWUgYWxsIHRoZSBwb2ludHMgCgpgYGB7cn0KZGF0YShtcGcsIHBhY2thZ2UgPSAiZ2dwbG90MiIpICAjIGFsdGVybmF0ZSBzb3VyY2U6ICdodHRwOi8vZ29vLmdsL3VFZVJHdScpCnRoZW1lX3NldCh0aGVtZV9idygpKQpnIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpCgpnICtnZW9tX2ppdHRlcih3aWR0aCA9IDAuNSwgc2l6ZSA9IDEpICsgCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsc2UgPSBGQUxTRSkgKyAKICAgICAgbGFicyhzdWJ0aXRsZSA9ICJtcGc6IGNpdHkgdnMgaGlnaHdheSBtaWxlYWdlIiwgeSA9ICJod3kiLCB4ID0gImN0eSIsIHRpdGxlID0gIkppdHRlcmVkIFBvaW50cyIpCmBgYAojIyMgY291bnRzIGNoYXJ0IAppbnN0ZWFkIG9mIGFkZGluZyBub2lzZSB3ZSBjYW4gZG8gYSBiaWdnZXIgcG9pbnQgd2hlbiB0aGVyIGlzIG92ZXJsYXBwaW5nIAoKYGBge3J9CmcgKyBnZW9tX2NvdW50KGNvbCA9ICJ0b21hdG8zIiwgc2hvdy5sZWdlbmQgPSBUUlVFKSArIAogICAgbGFicyhzdWJ0aXRsZSA9ICJtcGc6IGNpdHkgdnMgaGlnaHdheSBtaWxlYWdlIiwgeSA9ICJod3kiLCB4ID0gImN0eSIsIHRpdGxlID0gIkNvdW50cyBQbG90IikKYGBgCgojIyMgQnViYmxlIHBsb3QgCnNjYXR0ZXIgaXMgZm9yIGNvbXBhcmluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIGNvbnRpbnVvcyB2YXJpYWJsZXMgd2hpbGUgYSBidWJibGUgaWYgeW91IHdhbnQgdGhlIHJlbGF0aW9uc2hpcCB3aGl0aGluIHRoZSBncm91cCAKYmFzZWQgb24gOiAxKSBhIGNhdGVnb3JpY2FsIHZhbHVlIChjb2xvcikgMikgYSBjb250b251b3MgdmFyaWFibGUgKCBzaXplKQoKYGBge3J9CiMgc2VsZWN0IDQgbWFudWZhY3R1cmVycyAKbXBnX3NlbGVjdCA8LSBtcGcgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG1hbnVmYWN0dXJlciAlaW4lIGMoImF1ZGkiLCAiZm9yZCIsICJob25kYSIsCiAgICAgICAgImh5dW5kYWkiKSkKCiMgYmFzaWMgcGxvdCAKZyA8LSBnZ3Bsb3QobXBnX3NlbGVjdCwgYWVzKGRpc3BsLCBjdHkpKSArIGxhYnMoc3VidGl0bGUgPSAibXBnOiBDaXR5IE1pbGVhZ2UgdnMuIERpc3BsYWNlbWVudCIsIHRpdGxlID0gIkJ1YmJsZSBjaGFydCIpCgoKZyArIGdlb21faml0dGVyKGFlcyhjb2wgPSBtYW51ZmFjdHVyZXIsIHNpemUgPSBod3kpKSArICMgY29sIGlzIG1hbmlmYWN0dXJlciB3aGlsZSBzaXplIGlzIHRoZSBoaWdod2F5IG1pbGVhaGUgCiAgICBnZW9tX3Ntb290aChhZXMoY29sID0gbWFudWZhY3R1cmVyKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGKSAjIGFkZCBsaW5lIApgYGAKIyMjIG1hcmdpbmFsIGhpc3RvZ3JhbS8gYm94cGxvdCAKcmVsYXRpb25zaGlwIGFuZCBkaXN0cmlidXRpb24gaW4gdGhlIHNhbWUgZ3JhcGgKYGBge3J9CnBfbG9hZChnZ0V4dHJhKQoKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5LCBod3kpKSArIAogICAgIGdlb21fY291bnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAjIHNpemUgCiAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGKSAjIGxpbmUgCgpnZ01hcmdpbmFsKGcsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpICMgYWRkIG1hcmdpbmFsICBkaXN0cmlidXRpb24KZ2dNYXJnaW5hbChnLCB0eXBlID0gImJveHBsb3QiLCBmaWxsID0gInRyYW5zcGFyZW50IikKZ2dNYXJnaW5hbChnLCB0eXBlID0gImRlbnNpdHkiLCBmaWxsID0gInRyYW5zcGFyZW50IikKZ2dNYXJnaW5hbChnLCB0eXBlID0gImRlbnNpZ3JhbSIpICAjIGRlbnNpdHkgKyBoaXN0b2dyYW0KCgpgYGAKIyMjIGNvcnJlbG9ncmFtCkNvcnJlbG9ncmFtcyBsZXQgeW91IGV4YW1pbmUgdGhlIGNvcnJlbGF0aW9uIG9mIG11bHRpcGxlIGNvbnRpbnVvdXMgdmFyaWFibGVzIHByZXNlbnQgaW4gdGhlIHNhbWUgZGF0YWZyYW1lCgpgYGB7cn0KcF9sb2FkKGdnY29ycnBsb3QpCgpkYXRhKG10Y2FycykKZGltKG10Y2FycykKIz4gWzFdIDMyIDExCiMgY29tcHV0ZSB0aGUgY29ycmVsYXRpb24gbWF0cml4CmNvcnIgPC0gcm91bmQoY29yKG10Y2FycyksIDEpCgojIHBsb3QKZ2djb3JycGxvdChjb3JyLCAKICAgICAgICAgICBoYy5vcmRlciA9IEYsICMgb3JkZXIgdGhlIGNvcnIuIG1hdHJpeCBieSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwogICAgICAgICAgIHR5cGUgPSAibG93ZXIiLCAKICAgICAgICAgICBsYWIgPSBUUlVFLCAjIGFkZCBjb3JyLiBjb2VmZmljaWVudHMKICAgICAgICAgICBsYWJfc2l6ZSA9IDMsIAogICAgICAgICAgIG1ldGhvZD0iY2lyY2xlIiwgCiAgICAgICAgICAgY29sb3JzID0gYygidG9tYXRvMiIsICJ3aGl0ZSIsICJzcHJpbmdncmVlbjMiKSwgIyBjb2xvcnMgZm9yIGxvdywgbWlkLCBoaWdoIGNvcnJlbGF0aW9uIHZhbHVlcwogICAgICAgICAgIHRpdGxlPSJDb3JyZWxvZ3JhbSBvZiBtdGNhcnMiLCAKICAgICAgICAgICBnZ3RoZW1lPXRoZW1lX2J3KQpgYGAKCgojIyBEZXZpYXRpb24gCmNvbXBhcmUgdmFyaWF0aW9uIGluIHZhbHVlcyBiZXR3ZWVuIHNtYWxsIG51bWJlciBvZiBpdGVtcyAKCiMjIyBkaXZlcmdpbmcgYmFycyAKYGBge3J9CmRhdGEoIm10Y2FycyIpCiMgZGF0YSBwcmVwCm10Y2FycyA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihtdGNhcnMsIHZhcj0iY2FyIG5hbWUiKSAlPiUgIyBjcmVhdGUgbmV3IGNvbHVtbiBmb3IgY2FyIG5hbWVzCiAgICAgICAgICBtdXRhdGUobXBnX3o9cm91bmQoc2NhbGUobXBnKSwgMiksICMgY29tcHV0ZSBub3JtYWxpemVkIG1wZwogICAgICAgICAgICAgICAgIG1wZ190eXBlPWlmZWxzZShtcGdfeiA8IDAsICJiZWxvdyIsICJhYm92ZSIpLCkgJT4lICAjIGFib3ZlIC8gYmVsb3cgYXZnIGZsYWcKICAgICAgICAgIGFycmFuZ2UobXBnX3opIyBzb3J0CgptdGNhcnMkYGNhciBuYW1lYCA8LSBmYWN0b3IobXRjYXJzJGBjYXIgbmFtZWAsIGxldmVscyA9IG10Y2FycyRgY2FyIG5hbWVgKSAgIyBjb252ZXJ0IHRvIGZhY3RvciB0byByZXRhaW4gc29ydGVkIG9yZGVyIGluIHBsb3QuCgojIGRpdmVyZ2luZyBiYXJzCmdncGxvdChtdGNhcnMsIGFlcyh4PWBjYXIgbmFtZWAsIHk9bXBnX3osIGxhYmVsPW1wZ196KSkgKyAKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgYWVzKGZpbGw9bXBnX3R5cGUpLCB3aWR0aD0uNSkgICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik1pbGVhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkFib3ZlIEF2ZXJhZ2UiLCAiQmVsb3cgQXZlcmFnZSIpLCAKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoImFib3ZlIj0iIzAwYmEzOCIsICJiZWxvdyI9IiNmODc2NmQiKSkgKyAKICAgIGxhYnMoc3VidGl0bGU9Ik5vcm1hbGl6ZWQgbWlsZWFnZSBmcm9tIG10Y2FycyIsIAogICAgICAgICB0aXRsZT0gIkRpdmVyZ2luZyBCYXJzIikgKyAKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB0aGVtZV9idygpCmBgYAoKIyMjIGRpdmVyZ2luZyBsb2xsaXBvcCBjaGFydCAKCmBgYHtyfQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGBjYXIgbmFtZWAsIHkgPSBtcGdfeiwgbGFiZWwgPSBtcGdfeikpICsKICAgICAgIGdlb21fcG9pbnQoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiYmxhY2siLCBzaXplID0gNikgKwogICAgICAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gMCwgeCA9IGBjYXIgbmFtZWAsIHllbmQgPSBtcGdfeiwgeGVuZCA9IGBjYXIgbmFtZWApLGNvbG9yID0gImJsdWUiKSArIAogICAgICAgZ2VvbV90ZXh0KGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDIpICsKICAgICAgIGxhYnModGl0bGUgPSAiRGl2ZXJnaW5nIExvbGxpcG9wIENoYXJ0Iiwgc3VidGl0bGUgPSAiTm9ybWFsaXplZCBtaWxlYWdlIGZyb20gbXRjYXJzOiBMb2xsaXBvcCIpICsKICAgICAgIHlsaW0oLTIuNSwgMi41KSArIGNvb3JkX2ZsaXAoKSArIHRoZW1lX2J3KCkKYGBgCiMjIyBkaXZlcmdpbmcgYm90IHBsb3QgCmBgYHtyfQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGBjYXIgbmFtZWAsIHkgPSBtcGdfeiwgbGFiZWwgPSBtcGdfeikpICsKICAgIGdlb21fcG9pbnQoc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhjb2wgPSBtcGdfdHlwZSksIHNpemUgPSA2KSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJNaWxlYWdlIiwgbGFiZWxzID0gYygiQWJvdmUgQXZlcmFnZSIsICJCZWxvdyBBdmVyYWdlIiksIHZhbHVlcyA9IGMoYWJvdmUgPSAiIzAwYmEzOCIsIGJlbG93ID0gIiNmODc2NmQiKSkgKwogICAgZ2VvbV90ZXh0KGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDIpICsgCiAgICBsYWJzKHRpdGxlID0gIkRpdmVyZ2luZyBEb3QgUGxvdCIsIHN1YnRpdGxlID0gIk5vcm1hbGl6ZWQgbWlsZWFnZSBmcm9tICdtdGNhcnMnOiBEb3RwbG90IikgKwogICAgeWxpbSgtMi41LCAyLjUpICsgY29vcmRfZmxpcCgpICsgdGhlbWVfYncoKQpgYGAKCiMjIyBhcmVhIGNoYXJ0IApgYGB7cn0KZGF0YSgiZWNvbm9taWNzIiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikKYGBgCgpgYGB7cn0KIyBDb21wdXRlICVSZXR1cm5zCmVjb25vbWljcyRyZXR1cm5zX3BlcmMgPC0gYygwLCBkaWZmKGVjb25vbWljcyRwc2F2ZXJ0KS9lY29ub21pY3MkcHNhdmVydFstbGVuZ3RoKGVjb25vbWljcyRwc2F2ZXJ0KV0pCgoKIyBDcmVhdGUgYnJlYWsgcG9pbnRzIGFuZCBsYWJlbHMgZm9yIGF4aXMgdGlja3MKYnJrcyA8LSBlY29ub21pY3MkZGF0ZVtzZXEoMSwgbGVuZ3RoKGVjb25vbWljcyRkYXRlKSwgMTIpXQpsYmxzIDwtIGx1YnJpZGF0ZTo6eWVhcihicmtzKQoKIyBwbG90IHRoZSAxc3QgMTAwIG9ic2VydmF0aW9ucwpnZ3Bsb3QoZWNvbm9taWNzWzE6MTAwLCBdLCBhZXMoZGF0ZSwgcmV0dXJuc19wZXJjKSkgKyBnZW9tX2FyZWEoKSArCiAgICBzY2FsZV94X2RhdGUoYnJlYWtzID0gYnJrcywgbGFiZWxzID0gbGJscykgKyBsYWJzKHRpdGxlID0gIkFyZWEgQ2hhcnQiLAogICAgc3VidGl0bGUgPSAiUGVyY2VudGFnZSBSZXR1cm5zIGZvciBQZXJzb25hbCBTYXZpbmdzIiwgeSA9ICIlIFJldHVybnMgZm9yIFBlcnNvbmFsIHNhdmluZ3MiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IGVjb25vbWljcyBkYXRhc2V0IikgKyB0aGVtZV9idygpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKCiMjIHJhbmtpbmcgCkEgcmFua2luZyBwbG90IGlzIHVzZWQgdG8gY29tcGFyZSB0aGUgcG9zaXRpb24gb3IgcGVyZm9ybWFuY2Ugb2YgbXVsdGlwbGUgaXRlbXMgd2l0aCByZXNwZWN0IHRvIGVhY2ggb3RoZXIuIEFjdHVhbCB2YWx1ZXMgbWF0dGVyIHNvbWV3aGF0IGxlc3MgdGhhbiB0aGUgcmFua2luZy4KCiMjIyBvcmRlcmVkIGJhciBjaGFydCAKYGBge3J9CiMgZGF0YSBwcmVwOiBncm91cCBtZWFuIGNpdHkgbWlsZWFnZSBieSBtYW51ZmFjdHVyZXIuCmN0eV9tcGcgPC0gbXBnICU+JSBncm91cF9ieShtYWtlID0gbWFudWZhY3R1cmVyKSAlPiUgc3VtbWFyaXNlKG1pbGVhZ2UgPSBtZWFuKGN0eSkpCmN0eV9tcGcgPC0gYXJyYW5nZShjdHlfbXBnLCBtaWxlYWdlKSAgIyBzb3J0CmN0eV9tcGckbWFrZSA8LSBmYWN0b3IoY3R5X21wZyRtYWtlLCBsZXZlbHMgPSBjdHlfbXBnJG1ha2UpICAjIHJlZmFjdG9yIHRvIHJldGFpbiB0aGUgb3JkZXIgaW4gcGxvdC4KCiMgRHJhdyBwbG90CmdncGxvdChjdHlfbXBnLCBhZXMoeCA9IG1ha2UsIHkgPSBtaWxlYWdlKSkgKyAKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSwgZmlsbCA9ICJ0b21hdG8zIikgKyBsYWJzKHRpdGxlID0gIk9yZGVyZWQgQmFyIENoYXJ0IiwKICAgIHN1YnRpdGxlID0gIk1ha2UgVnMgQXZnLiBNaWxlYWdlIiwgY2FwdGlvbiA9ICJzb3VyY2U6IG1wZyIpICsKICAgIHRoZW1lX2J3KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDY1LAogICAgdmp1c3QgPSAwLjYpKQoKYGBgCiMjIyBsb2xsaXBvcCBjaGFydCAKCmBgYHtyfQojIERyYXcgcGxvdApnZ3Bsb3QoY3R5X21wZywgYWVzKHggPSBtYWtlLCB5ID0gbWlsZWFnZSkpICsgZ2VvbV9wb2ludChzaXplID0gMykgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbWFrZSwgeGVuZCA9IG1ha2UsIHkgPSAwLCB5ZW5kID0gbWlsZWFnZSkpICsKICAgIGxhYnModGl0bGUgPSAiTG9sbGlwb3AgQ2hhcnQiLCBzdWJ0aXRsZSA9ICJNYWtlIFZzIEF2Zy4gTWlsZWFnZSIsCiAgICAgICAgY2FwdGlvbiA9ICJzb3VyY2U6IG1wZyIpICsgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjUsCiAgICB2anVzdCA9IDAuNikpCmBgYAojIyMgZG90IHBsb3QgCgpgYGB7cn0KZ2dwbG90KGN0eV9tcGcsIGFlcyh4PW1ha2UsIHk9bWlsZWFnZSkpICsgCiAgICBnZW9tX3BvaW50KGNvbD0idG9tYXRvMiIsIHNpemU9MykgKyAjIGRyYXcgcG9pbnRzCiAgICBnZW9tX3NlZ21lbnQoYWVzKHg9bWFrZSwgCiAgICAgICAgICAgICAgICAgICAgIHhlbmQ9bWFrZSwgCiAgICAgICAgICAgICAgICAgICAgIHk9bWluKG1pbGVhZ2UpLCAKICAgICAgICAgICAgICAgICAgICAgeWVuZD1tYXgobWlsZWFnZSkpLCAKICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgIyBkcmF3IGRhc2hlZCBsaW5lcwogICAgICAgICAgICAgICAgIHNpemU9MC4xKSArICAgCiAgICBsYWJzKHRpdGxlPSJEb3QgUGxvdCIsIAogICAgICAgICBzdWJ0aXRsZT0iTWFrZSBWcyBBdmcuIE1pbGVhZ2UiLCAKICAgICAgICAgY2FwdGlvbj0ic291cmNlOiBtcGciKSArICAKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCiMjIyBzbG9wZSBjaGFydCAKCmBgYHtyfQpsaWJyYXJ5KHNjYWxlcykKCiMgZGF0YSBwcmVwCmRhdGFmIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2VsdmE4Ni9kYXRhc2V0cy9tYXN0ZXIvZ2RwcGVyY2FwLmNzdiIpCmNvbG5hbWVzKGRhdGFmKSA8LSBjKCJjb250aW5lbnQiLCAiMTk1MiIsICIxOTU3IikKIyBwcmVwYXJlIGxhYmVscwpsZWZ0X2xhYmVsIDwtIHBhc3RlKGRhdGFmJGNvbnRpbmVudCwgcm91bmQoZGF0YWYkYDE5NTJgKSwgc2VwPSIsICIpCnJpZ2h0X2xhYmVsIDwtIHBhc3RlKGRhdGFmJGNvbnRpbmVudCwgcm91bmQoZGF0YWYkYDE5NTdgKSwgc2VwPSIsICIpCmRhdGFmIDwtIGRhdGFmICU+JSBtdXRhdGUoY2xhc3M9aWZlbHNlKGAxOTU3YCAtIGAxOTUyYCA8IDAsICJyZWQiLCAiZ3JlZW4iKSkKCnAgPC0gZ2dwbG90KGRhdGFmKSArIGdlb21fc2VnbWVudChhZXMoeD0xLCB4ZW5kPTIsIHk9YDE5NTJgLCB5ZW5kPWAxOTU3YCwgY29sPWNsYXNzKSwgc2l6ZT0uNzUsIHNob3cubGVnZW5kPUYpICsgCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MSwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9LjEpICsgCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MiwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9LjEpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCJVcCIsICJEb3duIiksIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoImdyZWVuIj0iIzAwYmEzOCIsICJyZWQiPSIjZjg3NjZkIikpICsgICMgY29sb3Igb2YgbGluZXMKICAgIGxhYnMoeD0iIiwgeT0iTWVhbiBHZHBQZXJDYXAiKSArICAjIEF4aXMgbGFiZWxzCiAgICB4bGltKC41LCAyLjUpICsgeWxpbSgwLCgxLjEqKG1heChkYXRhZiRgMTk1MmAsIGRhdGFmJGAxOTU3YCkpKSkgKwogICAgdGhlbWVfY2xhc3NpYygpCgojIGFkZCB0ZXh0cwpwIDwtIHAgKyBnZW9tX3RleHQobGFiZWw9bGVmdF9sYWJlbCwgeT1kYXRhZiRgMTk1MmAsIHg9cmVwKDEsIE5ST1coZGF0YWYpKSwgaGp1c3Q9MS4xLCBzaXplPTMuNSkKcCA8LSBwICsgZ2VvbV90ZXh0KGxhYmVsPXJpZ2h0X2xhYmVsLCB5PWRhdGFmJGAxOTU3YCwgeD1yZXAoMiwgTlJPVyhkYXRhZikpLCBoanVzdD0tMC4xLCBzaXplPTMuNSkKcCA8LSBwICsgZ2VvbV90ZXh0KGxhYmVsPSJUaW1lIDEiLCB4PTEsIHk9MS4xKihtYXgoZGF0YWYkYDE5NTJgLCBkYXRhZiRgMTk1N2ApKSwgaGp1c3Q9MS4yLCBzaXplPTUpICAjIHRpdGxlCnAgPC0gcCArIGdlb21fdGV4dChsYWJlbD0iVGltZSAyIiwgeD0yLCB5PTEuMSoobWF4KGRhdGFmJGAxOTUyYCwgZGF0YWYkYDE5NTdgKSksIGhqdXN0PS0wLjEsIHNpemU9NSkgICMgdGl0bGUKCiMgTWluaWZ5IHRoZW1lCnAgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDIsMSwyKSwgImNtIikpCmBgYAojIyMgZHVtYmRlbGwgcGxvdCAKCmBgYHtyfQpsaWJyYXJ5KGdnYWx0KQoKaGVhbHRoIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2VsdmE4Ni9kYXRhc2V0cy9tYXN0ZXIvaGVhbHRoLmNzdiIpCmhlYWx0aCRBcmVhIDwtIGZhY3RvcihoZWFsdGgkQXJlYSwgbGV2ZWxzID0gYXMuY2hhcmFjdGVyKGhlYWx0aCRBcmVhKSkgICMgZm9yIHRoZSBjb3JyZWN0IG9yZGVyaW5nIG9mIHRoZSBkdW1iYmVsbHMKCmdncGxvdChoZWFsdGgsIGFlcyh4ID0gcGN0XzIwMTQsIHhlbmQgPSBwY3RfMjAxMywgeSA9IEFyZWEsIGdyb3VwID0gQXJlYSkpICsKICAgIGdlb21fZHVtYmJlbGwoY29sb3IgPSAiI2EzYzRkYyIsIHNpemUgPSAwLjc1LCBjb2xvdXJfeGVuZCA9ICIjMGU2NjhiIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KSArIGxhYnMoeCA9IE5VTEwsCiAgICB5ID0gTlVMTCwgdGl0bGUgPSAiRHVtYmJlbGwgQ2hhcnQiLCBzdWJ0aXRsZSA9ICJQY3QgQ2hhbmdlOiAyMDEzIHZzIDIwMTQiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IGh0dHBzOi8vZ2l0aHViLmNvbS9ocmJybXN0ci9nZ2FsdCIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsCiAgICBmYWNlID0gImJvbGQiKSwgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI2Y3ZjdmNyIpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNmN2Y3ZjciKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgojIyBEaXN0cmlidXRpb24gCgojIyMgaGlzdG9ncmFtCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkgIyBzZXQgdGhlIHRoZW1lIGJlZm9yZWhhbmQKCiMgaGlzdG9ncmFtIG9uIGEgY29udGludW91cyAobnVtZXJpYykgdmFyaWFibGUKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoZGlzcGwpKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKQoKZyArIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPWNsYXNzKSwgCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IC4xLCAjIGNoYW5nZSBiaW53aWR0aAogICAgICAgICAgICAgICAgICAgY29sPSJibGFjayIsIAogICAgICAgICAgICAgICAgICAgc2l6ZT0uMSkgKyAgCiAgICBsYWJzKHRpdGxlPSJIaXN0b2dyYW0gd2l0aCBBdXRvIEJpbm5pbmciLCAKICAgICAgICAgc3VidGl0bGU9IkVuZ2luZSBEaXNwbGFjZW1lbnQgYWNyb3NzIFZlaGljbGUgQ2xhc3NlcyIpIApgYGAKYGBge3J9CmcgKyBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1jbGFzcyksIAogICAgICAgICAgICAgICAgICAgYmlucz01LCAjIGNoYW5nZSBudW1iZXIgb2YgYmlucwogICAgICAgICAgICAgICAgICAgY29sPSJibGFjayIsIAogICAgICAgICAgICAgICAgICAgc2l6ZT0uMSkgKwogIGxhYnModGl0bGU9Ikhpc3RvZ3JhbSB3aXRoIEZpeGVkIEJpbnMiLCAKICAgICAgIHN1YnRpdGxlPSJFbmdpbmUgRGlzcGxhY2VtZW50IGFjcm9zcyBWZWhpY2xlIENsYXNzZXMiKSAKYGBgCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKCiMgSGlzdG9ncmFtIG9uIGEgQ2F0ZWdvcmljYWwgdmFyaWFibGUKZyA8LSBnZ3Bsb3QobXBnLCBhZXMobWFudWZhY3R1cmVyKSkKCmcgKyBnZW9tX2JhcihhZXMoZmlsbCA9IGNsYXNzKSwgd2lkdGggPSAwLjUpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2NSwKICAgIHZqdXN0ID0gMC42KSkgKyBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvbiBDYXRlZ29yaWNhbCBWYXJpYWJsZSIsCiAgICBzdWJ0aXRsZSA9ICJNYW51ZmFjdHVyZXIgYWNyb3NzIFZlaGljbGUgQ2xhc3NlcyIpCmBgYAoKIyMjIGRlbnNpdHkgcGxvdCAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5KSkKCmcgKyBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBmYWN0b3IoY3lsKSksIGFscGhhID0gMC44KSArIGxhYnModGl0bGUgPSAiRGVuc2l0eSBwbG90IiwKICAgIHN1YnRpdGxlID0gIkNpdHkgTWlsZWFnZSBHcm91cGVkIGJ5IE51bWJlciBvZiBjeWxpbmRlcnMiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIsIHggPSAiQ2l0eSBNaWxlYWdlIiwgZmlsbCA9ICIjIEN5bGluZGVycyIpCmBgYAojIyMgYm94IHBsb3QgCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKCmcgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBjdHkpKQoKZyArIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFRSVUUsIGZpbGwgPSAicGx1bSIpICsgbGFicyh0aXRsZSA9ICJCb3ggcGxvdCIsCiAgICBzdWJ0aXRsZSA9ICJDaXR5IE1pbGVhZ2UgZ3JvdXBlZCBieSBDbGFzcyBvZiB2ZWhpY2xlIiwgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIsCiAgICB4ID0gIkNsYXNzIG9mIFZlaGljbGUiLCB5ID0gIkNpdHkgTWlsZWFnZSIpCgoKZyArIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGZhY3RvcihjeWwpKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDY1LAogICAgdmp1c3QgPSAwLjYpKSArIGxhYnModGl0bGUgPSAiQm94IHBsb3QiLCBzdWJ0aXRsZSA9ICJDaXR5IE1pbGVhZ2UgZ3JvdXBlZCBieSBDbGFzcyBvZiB2ZWhpY2xlIiwKICAgIGNhcHRpb24gPSAiU291cmNlOiBtcGciLCB4ID0gIkNsYXNzIG9mIFZlaGljbGUiLCB5ID0gIkNpdHkgTWlsZWFnZSIpCmBgYAojIyMgZG90ICsgYm94IHBsb3QgCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgpnIDwtIGdncGxvdChtcGcsIGFlcyhtYW51ZmFjdHVyZXIsIGN0eSkpCgpnICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2RvdHBsb3QoYmluYXhpcyA9ICJ5Iiwgc3RhY2tkaXIgPSAiY2VudGVyIiwKICAgIGRvdHNpemUgPSAwLjUsIGZpbGwgPSAicmVkIikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDY1LAogICAgdmp1c3QgPSAwLjYpKSArIGxhYnModGl0bGUgPSAiQm94IHBsb3QgKyBEb3QgcGxvdCIsIHN1YnRpdGxlID0gIkNpdHkgTWlsZWFnZSB2cyBDbGFzczogRWFjaCBkb3QgcmVwcmVzZW50cyAxIHJvdyBpbiBzb3VyY2UgZGF0YSIsCiAgICBjYXB0aW9uID0gIlNvdXJjZTogbXBnIiwgeCA9ICJDbGFzcyBvZiBWZWhpY2xlIiwgeSA9ICJDaXR5IE1pbGVhZ2UiKQoKZyArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gTkEpICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMiksCiAgICBzaXplID0gMSwgY29sb3IgPSAicmVkIikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDY1LAogICAgdmp1c3QgPSAwLjYpKSArIGxhYnModGl0bGUgPSAiQm94IHBsb3QgKyBEb3QgcGxvdCIsIHN1YnRpdGxlID0gIkNpdHkgTWlsZWFnZSB2cyBDbGFzczogRWFjaCBkb3QgcmVwcmVzZW50cyAxIHJvdyBpbiBzb3VyY2UgZGF0YSIsCiAgICBjYXB0aW9uID0gIlNvdXJjZTogbXBnIiwgeCA9ICJDbGFzcyBvZiBWZWhpY2xlIiwgeSA9ICJDaXR5IE1pbGVhZ2UiKQpgYGAKIyMjIHR1ZnRlIGJveHBsb3QKCmBgYHtyfQpwX2xvYWQoZ2d0aGVtZXMpCnRoZW1lX3NldCh0aGVtZV90dWZ0ZSgpKQoKZyA8LSBnZ3Bsb3QobXBnLCBhZXMobWFudWZhY3R1cmVyLCBjdHkpKQoKZyArIGdlb21fdHVmdGVib3hwbG90KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDY1LAogICAgdmp1c3QgPSAwLjYpKSArIGxhYnModGl0bGUgPSAiVHVmdGUgU3R5bGVkIEJveHBsb3QiLCBzdWJ0aXRsZSA9ICJDaXR5IE1pbGVhZ2UgZ3JvdXBlZCBieSBDbGFzcyBvZiB2ZWhpY2xlIiwKICAgIGNhcHRpb24gPSAiU291cmNlOiBtcGciLCB4ID0gIkNsYXNzIG9mIFZlaGljbGUiLCB5ID0gIkNpdHkgTWlsZWFnZSIpCmBgYAojIyMgdmlvbGluIHBsb3QgCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCmcgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBjdHkpKQoKZyArIGdlb21fdmlvbGluKCkgKyBsYWJzKHRpdGxlID0gIlZpb2xpbiBwbG90Iiwgc3VidGl0bGUgPSAiQ2l0eSBNaWxlYWdlIHZzIENsYXNzIG9mIHZlaGljbGUiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIsIHggPSAiQ2xhc3Mgb2YgVmVoaWNsZSIsIHkgPSAiQ2l0eSBNaWxlYWdlIikKYGBgCiMjIyBwb3B1bGF0aW9uIHBpcmFtaWQgCmBgYHtyfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgICMgdHVybnMgb2Ygc2NpZW50aWZpYyBub3RhdGlvbnMgbGlrZSAxZSs0MAoKIyBnZXQgZGF0YQplbWFpbF9jYW1wYWlnbl9mdW5uZWwgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zZWx2YTg2L2RhdGFzZXRzL21hc3Rlci9lbWFpbF9jYW1wYWlnbl9mdW5uZWwuY3N2IikKYGBgCgpgYGB7cn0KIyBYIGF4aXMgYnJlYWtzIApicmtzIDwtIHNlcSgtMTUwMDAwMDAsIDE1MDAwMDAwLCA1MDAwMDAwKQojIFggYXhpcyBsYWJlbHMKbGJscyA8LSBwYXN0ZTAoYXMuY2hhcmFjdGVyKGMoc2VxKDE1LCAwLCAtNSksIHNlcSg1LCAxNSwgNSkpKSwgIm0iKQoKIyBweXJhbWlkCmdncGxvdChlbWFpbF9jYW1wYWlnbl9mdW5uZWwsIGFlcyh4ID0gU3RhZ2UsIHkgPSBVc2VycywgZmlsbCA9IEdlbmRlcikpICsgIyBGaWxsIGNvbHVtbgogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gLjYpICsgICMgZHJhdyB0aGUgYmFycwogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGJya3MsICAgIyBCcmVha3MKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBsYmxzKSArICMgTGFiZWxzCiAgICBjb29yZF9mbGlwKCkgKyAgIyBGbGlwIGF4ZXMKICAgIGxhYnModGl0bGU9IkVtYWlsIENhbXBhaWduIEZ1bm5lbCIpICsKICAgIHRoZW1lX3R1ZnRlKCkgKyAgIyBUdWZ0ZSB0aGVtZSBmcm9tIGdndGhlbWVzCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUpLCAjIENlbnRlciBwbG90IHRpdGxlCiAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgICMgQ29sb3IgcGFsZXR0ZQpgYGAKIyMgQ29tcG9zaXRpb24gCgojIyMgd2FmZmxlIGNoYXJ0IAoKYGBge3J9CnZhciA8LSBtcGckY2xhc3MgICMgY2F0ZWdvcmljYWwgZGF0YSAKdGFibGUodmFyKSAgIyBvcmlnaW5hbCBjYXRlZ29yeSBkaXN0cmlidXRpb24KIz4gdmFyCiM+ICAgIDJzZWF0ZXIgICAgY29tcGFjdCAgICBtaWRzaXplICAgIG1pbml2YW4gICAgIHBpY2t1cCBzdWJjb21wYWN0ICAgICAgICBzdXYgCiM+ICAgICAgICAgIDUgICAgICAgICA0NyAgICAgICAgIDQxICAgICAgICAgMTEgICAgICAgICAzMyAgICAgICAgIDM1ICAgICAgICAgNjIKIyBkYXRhIHByZXAKbnJvd3MgPC0gMTAgICMgb3VyIHdhZmZsZSBjaGFydCB3aWxsIGJlIGEgMTB4MTAgc3F1YXJlCmRhdGFmIDwtIGV4cGFuZC5ncmlkKHkgPSAxOm5yb3dzLCB4ID0gMTpucm93cykKY2F0ZWdfdGFibGUgPC0gcm91bmQodGFibGUodmFyKSAqICgobnJvd3MgKiBucm93cykvKGxlbmd0aCh2YXIpKSkpICAjIHRyYW5zZm9ybSB0aGUgY2F0ZWdvcnkgZGlzdHJpYnV0aW9uIHNvIHRoYXQgdGhlIGNvdW50cyBzdW0gdXAgdG8gMTAwCmNhdGVnX3RhYmxlCiM+IHZhcgojPiAgICAyc2VhdGVyICAgIGNvbXBhY3QgICAgbWlkc2l6ZSAgICBtaW5pdmFuICAgICBwaWNrdXAgc3ViY29tcGFjdCAgICAgICAgc3V2IAojPiAgICAgICAgICAyICAgICAgICAgMjAgICAgICAgICAxOCAgICAgICAgICA1ICAgICAgICAgMTQgICAgICAgICAxNSAgICAgICAgIDI2CiMgPiAyc2VhdGVyIGNvbXBhY3QgbWlkc2l6ZSBtaW5pdmFuIHBpY2t1cCBzdWJjb21wYWN0IHN1diA+CiMgMiAyMCAxOCA1IDE0IDE1IDI2CnN1bShjYXRlZ190YWJsZSkKIz4gWzFdIDEwMAoKZGF0YWYkY2F0ZWdvcnkgPC0gZmFjdG9yKHJlcChuYW1lcyhjYXRlZ190YWJsZSksIGNhdGVnX3RhYmxlKSkKIyBOT1RFOiBpZiBzdW0oY2F0ZWdfdGFibGUpIGlzIG5vdCAxMDAgKGkuZS4gbnJvd3NeMiksIGl0CiMgd2lsbCBuZWVkIGFkanVzdG1lbnQgdG8gbWFrZSB0aGUgc3VtIHRvIDEwMC4KCiMgd2FmZmxlIGNoYXJ0CmdncGxvdChkYXRhZiwgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IGNhdGVnb3J5KSkgKyBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siLAogICAgc2l6ZSA9IDAuNSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLAogICAgMCksIHRyYW5zID0gInJldmVyc2UiKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpICsKICAgIGxhYnModGl0bGUgPSAiV2FmZmxlIENoYXJ0Iiwgc3VidGl0bGUgPSAiJ0NsYXNzJyBvZiB2ZWhpY2xlcyIsCiAgICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIpICsgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KHNpemUgPSAyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjIpKSwgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCiMjIyBwaWUgY2hhcnQgCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKIyBTb3VyY2U6IEZyZXF1ZW5jeSB0YWJsZQpkYXRhZiA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG1wZyRjbGFzcykpCmNvbG5hbWVzKGRhdGFmKSA8LSBjKCJjbGFzcyIsICJmcmVxIikKCnBpZSA8LSBnZ3Bsb3QoZGF0YWYsIGFlcyh4ID0gIiIsIHkgPSBmcmVxLCBmaWxsID0gZmFjdG9yKGNsYXNzKSkpICsKICAgIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgbGFicyhmaWxsID0gImNsYXNzIiwKICAgIHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiUGllIENoYXJ0IG9mIGNsYXNzIiwgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIpCgojIHdoYXQgd2UgZ290IHNvIGZhcgpwaWUgKyBjb29yZF9wb2xhcih0aGV0YSA9ICJ5Iiwgc3RhcnQgPSAwKSArIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQpgYGAKIyMjIHRyZWVtYXAKCmBgYHtyfQpwX2xvYWQodHJlZW1hcGlmeSkKZ2dwbG90KEcyMCwgYWVzKGFyZWEgPSBnZHBfbWlsX3VzZCwgZmlsbCA9IGhkaSwgbGFiZWwgPSBjb3VudHJ5LCBzdWJncm91cCA9IHJlZ2lvbikpICsKICAgICAgIGdlb21fdHJlZW1hcCgpICsgZ2VvbV90cmVlbWFwX3N1Ymdyb3VwX2JvcmRlcigpICsKICAgICAgIGdlb21fdHJlZW1hcF9zdWJncm91cF90ZXh0KHBsYWNlID0gImNlbnRyZSIsIGdyb3cgPSBULCBhbHBoYSA9IDAuNSwKICAgICAgIGNvbG91ciA9ICJibGFjayIsIGZvbnRmYWNlID0gIml0YWxpYyIsIG1pbi5zaXplID0gMCkgKwogICAgICAgZ2VvbV90cmVlbWFwX3RleHQoY29sb3VyID0gIndoaXRlIiwgcGxhY2UgPSAidG9wbGVmdCIsIHJlZmxvdyA9IFQpCgoKZ2dwbG90KEcyMCwgYWVzKGFyZWEgPSAxLCBsYWJlbCA9IGNvdW50cnksIHN1Ymdyb3VwID0gaGVtaXNwaGVyZSwKICAgIHN1Ymdyb3VwMiA9IHJlZ2lvbiwgc3ViZ3JvdXAzID0gZWNvbl9jbGFzc2lmaWNhdGlvbikpICsgZ2VvbV90cmVlbWFwKCkgKwogICAgZ2VvbV90cmVlbWFwX3N1Ymdyb3VwM19ib3JkZXIoY29sb3VyID0gImJsdWUiLCBzaXplID0gMSkgKwogICAgZ2VvbV90cmVlbWFwX3N1Ymdyb3VwMl9ib3JkZXIoY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICAgIGdlb21fdHJlZW1hcF9zdWJncm91cF9ib3JkZXIoY29sb3VyID0gInJlZCIsIHNpemUgPSA1KSArCiAgICBnZW9tX3RyZWVtYXBfc3ViZ3JvdXBfdGV4dChwbGFjZSA9ICJtaWRkbGUiLCBjb2xvdXIgPSAicmVkIiwKICAgICAgICBhbHBoYSA9IDAuNSwgZ3JvdyA9IFQpICsgZ2VvbV90cmVlbWFwX3N1Ymdyb3VwMl90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIsCiAgICBhbHBoYSA9IDAuNSwgZm9udGZhY2UgPSAiaXRhbGljIikgKyBnZW9tX3RyZWVtYXBfc3ViZ3JvdXAzX3RleHQocGxhY2UgPSAidG9wIiwKICAgIGNvbG91ciA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsgZ2VvbV90cmVlbWFwX3RleHQoY29sb3VyID0gIndoaXRlIiwKICAgIHBsYWNlID0gIm1pZGRsZSIsIHJlZmxvdyA9IFQpCmBgYAojIyMgYmFyIGNoYXJ0IAoKYGBge3J9CiMgZGF0YSBwcmVwOiBmcmVxdWVuY3kgdGFibGUKZnJlcXRhYmxlIDwtIHRhYmxlKG1wZyRtYW51ZmFjdHVyZXIpCmRhdGFmIDwtIGFzLmRhdGEuZnJhbWUudGFibGUoZnJlcXRhYmxlKSAlPiUKICAgIHJlbmFtZShtYW51ZmFjdHVyZXIgPSBWYXIxKQoKYGBgCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKZyA8LSBnZ3Bsb3QoZGF0YWYsIGFlcyhtYW51ZmFjdHVyZXIsIEZyZXEpKQpnICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC41LCBmaWxsID0gInRvbWF0bzIiKSArCiAgICBsYWJzKHRpdGxlID0gIkJhciBDaGFydCIsIHN1YnRpdGxlID0gIk1hbnVmYWN0dXJlciBvZiB2ZWhpY2xlcyIsCiAgICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEZyZXF1ZW5jeSBvZiBNYW51ZmFjdHVyZXJzIGZyb20gJ21wZycgZGF0YXNldCIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjUsIHZqdXN0ID0gMC42KSkKCgpnIDwtIGdncGxvdChtcGcsIGFlcyhtYW51ZmFjdHVyZXIpKQpnICsgZ2VvbV9iYXIoYWVzKGZpbGw9Y2xhc3MpLCB3aWR0aCA9IDAuNSkgKyAjIGZpbGwgYnkgY2xhc3MKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjYpKSArCiAgICBsYWJzKHRpdGxlPSJDYXRlZ29yeXdpc2UgQmFyIENoYXJ0IiwgCiAgICAgICAgIHN1YnRpdGxlPSJNYW51ZmFjdHVyZXIgb2YgdmVoaWNsZXMiLCAKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBNYW51ZmFjdHVyZXJzIGZyb20gJ21wZycgZGF0YXNldCIpCmBgYAojIyBDaGFuZ2UKCiMjIyBmcm9tIGEgdGltZSBzZXJpZSBvYmplY3QgCgpgYGB7cn0KcF9sb2FkKGdnZm9ydGlmeSkKcF9sb2FkKHRpZHl2ZXJzZSkKcF9sb2FkKHpvbykKIyBsb2FkIGRhdGEKZGF0YSgiQWlyUGFzc2VuZ2VycyIpCiMgY2hlY2sgdGhleSBhcmUgYSB0cyBvYmplY3QKY2xhc3MoQWlyUGFzc2VuZ2VycykKYGBgCgpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKCmF1dG9wbG90KEFpclBhc3NlbmdlcnMpICsgbGFicyh0aXRsZSA9ICJBaXJQYXNzZW5nZXJzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgoKIyMjIGZyb20gYSBkYXRhZ2ZyYW1lCgpgYGB7cn0KZGF0YShlY29ub21pY3MpCgojIChyZSljb21wdXRlICVSZXR1cm5zCmVjb25vbWljcyRyZXR1cm5zX3BlcmMgPC0gYygwLCBkaWZmKGVjb25vbWljcyRwc2F2ZXJ0KS9lY29ub21pY3MkcHNhdmVydFstbGVuZ3RoKGVjb25vbWljcyRwc2F2ZXJ0KV0pCgp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKIyBBbGxvdyBEZWZhdWx0IFggQXhpcyBMYWJlbHMKZ2dwbG90KGVjb25vbWljcywgYWVzKHggPSBkYXRlKSkgKyBnZW9tX2xpbmUoYWVzKHkgPSByZXR1cm5zX3BlcmMpKSArCiAgICBsYWJzKHRpdGxlID0gIlRpbWUgU2VyaWVzIENoYXJ0Iiwgc3VidGl0bGUgPSAiUmV0dXJucyBQZXJjZW50YWdlIGZyb20gJ0Vjb25vbWljcycgRGF0YXNldCIsCiAgICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEVjb25vbWljcyIsIHkgPSAiUmV0dXJucyAlIikKYGBgCgojIyMgZnJvbSBhIG1vbnRobHkgdGltZSBzZXJpZXMgCmBgYHtyfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGNvbnNpZGVyIGEgMjQtbW9udGggdGltZWZyYW1lCmVjb25vbWljc19tIDwtIGVjb25vbWljc1sxOjI0LCBdCiMgbGFiZWxzIGFuZCBicmVha3MgZm9yIFggYXhpcyB0ZXh0CmxibHMgPC0gcGFzdGUwKG1vbnRoLmFiYlttb250aChlY29ub21pY3NfbSRkYXRlKV0sICIgIiwgbHVicmlkYXRlOjp5ZWFyKGVjb25vbWljc19tJGRhdGUpKSAjIG1vbnRoLmFiYiBpcyBhIGJ1aWx0LWluIGNvbnN0YW50CmJya3MgPC0gZWNvbm9taWNzX20kZGF0ZQoKaGVhZChicmtzKQojPiBbMV0gIjE5NjctMDctMDEiICIxOTY3LTA4LTAxIiAiMTk2Ny0wOS0wMSIgIjE5NjctMTAtMDEiICIxOTY3LTExLTAxIgojPiBbNl0gIjE5NjctMTItMDEiCmhlYWQobGJscykKIz4gWzFdICJKdWwgMTk2NyIgIkF1ZyAxOTY3IiAiU2VwIDE5NjciICJPY3QgMTk2NyIgIk5vdiAxOTY3IiAiRGVjIDE5NjciCgojIHBsb3QKZ2dwbG90KGVjb25vbWljc19tLCBhZXMoeD1kYXRlKSkgKyAKICAgIGdlb21fbGluZShhZXMoeT1yZXR1cm5zX3BlcmMpKSArIAogICAgbGFicyh0aXRsZT0iTW9udGhseSBUaW1lIFNlcmllcyIsIAogICAgICAgICBzdWJ0aXRsZT0iUmV0dXJucyBQZXJjZW50YWdlIGZyb20gRWNvbm9taWNzIERhdGFzZXQiLCAKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBFY29ub21pY3MiLCAKICAgICAgICAgeT0iUmV0dXJucyAlIikgKyAgIyB0aXRsZSBhbmQgY2FwdGlvbgogICAgc2NhbGVfeF9kYXRlKGxhYmVscyA9IGxibHMsIAogICAgICAgICAgICAgICAgIGJyZWFrcyA9IGJya3MpICsgICMgY2hhbmdlIHRvIG1vbnRobHkgdGlja3MgYW5kIGxhYmVscwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3Q9MC41KSwgICMgcm90YXRlIHggYXhpcyB0ZXh0CiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAgCmBgYAojIyBmcm9tIGEgeWVhcmx5IHRpbWUgc2VyaWVzIAoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9idygpKQoKIyA3LjUgeWVhcnM6CmVjb25vbWljc195IDwtIGVjb25vbWljc1sxOjkwLCBdCgojIGxhYmVscyBhbmQgYnJlYWtzIGZvciBYIGF4aXMgdGV4dApicmtzIDwtIGVjb25vbWljc195JGRhdGVbc2VxKDEsIGxlbmd0aChlY29ub21pY3NfeSRkYXRlKSwgMTIpXSAjIG9uZSBicmVhayBhdCBlYWNoIHllYXIKbGJscyA8LSBsdWJyaWRhdGU6OnllYXIoYnJrcykKCiMgcGxvdApnZ3Bsb3QoZWNvbm9taWNzX3ksIGFlcyh4PWRhdGUpKSArIAogICAgZ2VvbV9saW5lKGFlcyh5PXJldHVybnNfcGVyYykpICsgCiAgICBsYWJzKHRpdGxlPSJZZWFybHkgVGltZSBTZXJpZXMiLCAKICAgICAgICAgc3VidGl0bGU9IlJldHVybnMgUGVyY2VudGFnZSBmcm9tIEVjb25vbWljcyBEYXRhc2V0IiwgCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogRWNvbm9taWNzIiwgCiAgICAgICAgIHk9IlJldHVybnMgJSIpICsgICMgdGl0bGUgYW5kIGNhcHRpb24KICAgIHNjYWxlX3hfZGF0ZShsYWJlbHMgPSBsYmxzLCAKICAgICAgICAgICAgICAgICBicmVha3MgPSBicmtzKSArICAjIGNoYW5nZSB0byBtb250aGx5IHRpY2tzIGFuZCBsYWJlbHMKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0PTAuNSksICAjIHJvdGF0ZSB4IGF4aXMgdGV4dAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgICMgdHVybiBvZmYgbWlub3IgZ3JpZApgYGAKIyMjIEZyb20gTG9uZyBEYXRhIEZvcm1hdDogTXVsdGlwbGUgVGltZSBTZXJpZXMgaW4gU2FtZSBEYXRhZnJhbWUgQ29sdW1uCmBgYHtyfQpkYXRhKGVjb25vbWljc19sb25nLCBwYWNrYWdlID0gImdncGxvdDIiKQpoZWFkKGVjb25vbWljc19sb25nKQpgYGAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKIyBmaWx0ZXIgJiByZXN0cmljdCB0byBzcGVjaWZpYyB5ZWFyIHJhbmdlCmRhdGFmIDwtIGVjb25vbWljc19sb25nICU+JSBkcGx5cjo6ZmlsdGVyKHZhcmlhYmxlICVpbiUgYygicHNhdmVydCIsICJ1ZW1wbWVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx1YnJpZGF0ZTo6eWVhcihkYXRlKSAlaW4lIGMoMTk2NzoxOTgxKSkKCnRhYmxlKGRhdGFmJHZhcmlhYmxlKQojPiAKIz4gcHNhdmVydCB1ZW1wbWVkIAojPiAgICAgMTc0ICAgICAxNzQKCiMgbGFiZWxzIGFuZCBicmVha3MgZm9yIFggYXhpcyB0ZXh0CmJya3MgPC0gZGF0YWYkZGF0ZVtzZXEoMSwgbGVuZ3RoKGRhdGFmJGRhdGUpLCAxMildICMgb25lIGJyZWFrIGF0IGVhY2ggeWVhcgpsYmxzIDwtIGx1YnJpZGF0ZTo6eWVhcihicmtzKQoKIyBwbG90CmdncGxvdChkYXRhZiwgYWVzKHg9ZGF0ZSkpICsgCiAgICBnZW9tX2xpbmUoYWVzKHk9dmFsdWUsIGNvbD12YXJpYWJsZSkpICsgCiAgICBsYWJzKHRpdGxlPSJUaW1lIFNlcmllcyBvZiBSZXR1cm5zIFBlcmNlbnRhZ2UiLCAKICAgICAgICAgc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IEVjb25vbWljcyIsIAogICAgICAgICBjb2xvcj1OVUxMKSArICAjIHRpdGxlIGFuZCBjYXB0aW9uCiAgICBzY2FsZV94X2RhdGUobGFiZWxzID0gbGJscywgYnJlYWtzID0gYnJrcykgKyAgIyBjaGFuZ2UgdG8gbW9udGhseSB0aWNrcyBhbmQgbGFiZWxzCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygicHNhdmVydCIsICJ1ZW1wbWVkIiksIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoInBzYXZlcnQiPSIjMDBiYTM4IiwgInVlbXBtZWQiPSIjZjg3NjZkIikpICsgICMgbGluZSBjb2xvcgogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3Q9MC41LCBzaXplID0gOCksICAjIHJvdGF0ZSB4IGF4aXMgdGV4dAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgICMgdHVybiBvZmYgbWlub3IgZ3JpZApgYGAKCiMjIyBGcm9tIFdpZGUgRGF0YSBGb3JtYXQ6IERhdGEgaW4gTXVsdGlwbGUgQ29sdW1ucyBvZiBEYXRhZnJhbWUKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCmRhdGFmIDwtIGVjb25vbWljcyAlPiUgZHBseXI6OnNlbGVjdChkYXRlLCBwc2F2ZXJ0LCB1ZW1wbWVkKSAlPiUgCiAgICBkcGx5cjo6ZmlsdGVyKGx1YnJpZGF0ZTo6eWVhcihkYXRlKSAlaW4lIGMoMTk2NzoxOTgxKSkKaGVhZChkYXRhZikKYGBgCgpgYGB7cn0KIyBsYWJlbHMgYW5kIGJyZWFrcyBmb3IgWCBheGlzIHRleHQKYnJrcyA8LSBkYXRhZiRkYXRlW3NlcSgxLCBsZW5ndGgoZGF0YWYkZGF0ZSksIDEyKV0KbGJscyA8LSBsdWJyaWRhdGU6OnllYXIoYnJrcykKCiMgcGxvdApnZ3Bsb3QoZGF0YWYsIGFlcyh4PWRhdGUpKSArIAogIGdlb21fbGluZShhZXMoeT1wc2F2ZXJ0LCBjb2w9InBzYXZlcnQiKSkgKyAjIDFzdCBsaW5lCiAgZ2VvbV9saW5lKGFlcyh5PXVlbXBtZWQsIGNvbD0idWVtcG1lZCIpKSArICMgMm5kIGxpbmUKICBsYWJzKHRpdGxlPSJUaW1lIFNlcmllcyBvZiBSZXR1cm5zIFBlcmNlbnRhZ2UiLCAKICAgICAgIHN1YnRpdGxlPSJEcmF3biBGcm9tIFdpZGUgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogRWNvbm9taWNzIiwgeT0idmFsdWUiKSArICAjIHRpdGxlIGFuZCBjYXB0aW9uCiAgc2NhbGVfeF9kYXRlKGxhYmVscyA9IGxibHMsIGJyZWFrcyA9IGJya3MpICsgICMgY2hhbmdlIHRvIG1vbnRobHkgdGlja3MgYW5kIGxhYmVscwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJwc2F2ZXJ0Ij0iIzAwYmEzOCIsICJ1ZW1wbWVkIj0iI2Y4NzY2ZCIpKSArICAjIGxpbmUgY29sb3IKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdD0wLjUsIHNpemUgPSA4KSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAgCmBgYAojIyMgc3RhY2tlZCBhcmVhIGNoYXJ0IAoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9idygpKQoKZGF0YWYgPC0gZWNvbm9taWNzICU+JSBkcGx5cjo6c2VsZWN0KGRhdGUsIHBzYXZlcnQsIHVlbXBtZWQpICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIobHVicmlkYXRlOjp5ZWFyKGRhdGUpICVpbiUgYygxOTY3OjE5ODEpKQoKIyBsYWJlbHMgYW5kIGJyZWFrcyBmb3IgWCBheGlzIHRleHQKYnJrcyA8LSBkYXRhZiRkYXRlW3NlcSgxLCBsZW5ndGgoZGF0YWYkZGF0ZSksIDEyKV0KbGJscyA8LSBsdWJyaWRhdGU6OnllYXIoYnJrcykKCiMgcGxvdApnZ3Bsb3QoZGF0YWYsIGFlcyh4PWRhdGUpKSArIAogICAgZ2VvbV9hcmVhKGFlcyh5PXBzYXZlcnQrdWVtcG1lZCwgZmlsbD0icHNhdmVydCIpKSArICMgMXN0ICJsYXllciIKICAgIGdlb21fYXJlYShhZXMoeT11ZW1wbWVkLCBmaWxsPSJ1ZW1wbWVkIikpICsgIyAybmQgImxheWVyIiAocGxvdHRlZCBvdmVyIHRoZSAxc3QpCiAgICBsYWJzKHRpdGxlPSJBcmVhIENoYXJ0IG9mIFJldHVybnMgUGVyY2VudGFnZSIsIAogICAgICAgICBzdWJ0aXRsZT0iRnJvbSBXaWRlIERhdGEgZm9ybWF0IiwgCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogRWNvbm9taWNzIiwgeT0idmFsdWUiKSArICAjIHRpdGxlIGFuZCBjYXB0aW9uCiAgICBzY2FsZV94X2RhdGUobGFiZWxzID0gbGJscywgYnJlYWtzID0gYnJrcykgKyAgIyBjaGFuZ2UgdG8gbW9udGhseSB0aWNrcyBhbmQgbGFiZWxzCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLCAKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoInBzYXZlcnQiPSIjMDBiYTM4IiwgInVlbXBtZWQiPSIjZjg3NjZkIikpICsgICMgbGluZSBjb2xvcgogICAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgICMgdHVybiBvZmYgbWlub3IgZ3JpZApgYGAKIyMjIGNhbGVuZGFyIGhlYXRtYXAKYGBge3J9CmxpYnJhcnkocGx5cikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoem9vKQoKZGF0YWYgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zZWx2YTg2L2RhdGFzZXRzL21hc3Rlci95YWhvby5jc3YiKSAgIyBZYWhvbyEgc3RvY2sgY2xvc2luZyBwcmljZSAyMDA3LTIwMTYKZGF0YWYkZGF0ZSA8LSBhcy5EYXRlKGRhdGFmJGRhdGUpICAjIGZvcm1hdCBkYXRlCmRhdGFmIDwtIGRhdGFmW2RhdGFmJHllYXIgPj0gMjAxMiwgXSAgIyBmaWx0ZXIgeWVhcnMKCiMgQ3JlYXRlIE1vbnRoIFdlZWsKZGF0YWYkeWVhcm1vbnRoIDwtIGFzLnllYXJtb24oZGF0YWYkZGF0ZSkKZGF0YWYkeWVhcm1vbnRoZiA8LSBmYWN0b3IoZGF0YWYkeWVhcm1vbnRoKQpkYXRhZiA8LSBkZHBseShkYXRhZiwgLih5ZWFybW9udGhmKSwgdHJhbnNmb3JtLCBtb250aHdlZWsgPSAxICsKICAgIHdlZWsgLSBtaW4od2VlaykpICAjIGNvbXB1dGUgd2VlayBudW1iZXIgb2YgbW9udGgKZGF0YWYgPC0gZGF0YWZbLCBjKCJ5ZWFyIiwgInllYXJtb250aGYiLCAibW9udGhmIiwgIndlZWsiLCAibW9udGh3ZWVrIiwKICAgICJ3ZWVrZGF5ZiIsICJWSVguQ2xvc2UiKV0KaGVhZChkYXRhZikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGFmLCBhZXMobW9udGh3ZWVrLCB3ZWVrZGF5ZiwgZmlsbCA9IFZJWC5DbG9zZSkpICsgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIpICsKICAgIGZhY2V0X2dyaWQoeWVhciB+IG1vbnRoZikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJyZWQiLAogICAgaGlnaCA9ICJncmVlbiIpICsgbGFicyh4ID0gIldlZWsgb2YgTW9udGgiLCB5ID0gIiIsIHRpdGxlID0gIlRpbWUtU2VyaWVzIENhbGVuZGFyIEhlYXRtYXAiLAogICAgc3VidGl0bGUgPSAiWWFob28gQ2xvc2luZyBQcmljZSIsIGZpbGwgPSAiQ2xvc2UiKQpgYGAKCiMjIyBzbG9wZSBjaGFydCAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpzb3VyY2VfZGYgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qa2VpcnN0ZWFkL3Itc2xvcGVncmFwaC9tYXN0ZXIvY2FuY2VyX3N1cnZpdmFsX3JhdGVzLmNzdiIpICAjIEVzdGltYXRlcyBvZiAlIHN1cnZpdmFsIHJhdGVzIGZvciBkaWZmZXJlbnQgdHVtb3JzCgojIERlZmluZSBmdW5jdGlvbnMuIFNvdXJjZToKIyBodHRwczovL2dpdGh1Yi5jb20vamtlaXJzdGVhZC9yLXNsb3BlZ3JhcGggQ2FsY3VsYXRlcwojIHNsb3BlIGdyYXBoIHBvc2l0aW9ucyBiYXNlZCBvbiBFZHdhcmQgVHVmdGUncyBsYXlvdXQKdHVmdGVfc29ydCA8LSBmdW5jdGlvbihkZiwgeCA9ICJ5ZWFyIiwgeSA9ICJ2YWx1ZSIsIGdyb3VwID0gImdyb3VwIiwKICAgIG1pbi5zcGFjZSA9IDAuMDUpIHsKICAgICMjIEZpcnN0IHJlbmFtZSB0aGUgY29sdW1ucyBmb3IgY29uc2lzdGVuY3kKICAgIGlkcyA8LSBtYXRjaChjKHgsIHksIGdyb3VwKSwgbmFtZXMoZGYpKQogICAgZGYgPC0gZGZbLCBpZHNdCiAgICBuYW1lcyhkZikgPC0gYygieCIsICJ5IiwgImdyb3VwIikKCiAgICAjIyBFeHBhbmQgZ3JpZCB0byBlbnN1cmUgZXZlcnkgY29tYmluYXRpb24gaGFzIGEKICAgICMjIGRlZmluZWQgdmFsdWUKICAgIHRtcCA8LSBleHBhbmQuZ3JpZCh4ID0gdW5pcXVlKGRmJHgpLCBncm91cCA9IHVuaXF1ZShkZiRncm91cCkpCiAgICB0bXAgPC0gbWVyZ2UoZGYsIHRtcCwgYWxsLnkgPSBUUlVFKQogICAgZGYgPC0gbXV0YXRlKHRtcCwgeSA9IGlmZWxzZShpcy5uYSh5KSwgMCwgeSkpCgogICAgIyMgQ2FzdCBpbnRvIGEgbWF0cml4IHNoYXBlIGFuZCBhcnJhbmdlIGJ5IGZpcnN0IGNvbHVtbgogICAgcmVxdWlyZShyZXNoYXBlMikKICAgIHRtcCA8LSBkY2FzdChkZiwgZ3JvdXAgfiB4LCB2YWx1ZS52YXIgPSAieSIpCiAgICBvcmQgPC0gb3JkZXIodG1wWywgMl0pCiAgICB0bXAgPC0gdG1wW29yZCwgXQoKICAgIG1pbi5zcGFjZSA8LSBtaW4uc3BhY2UgKiBkaWZmKHJhbmdlKHRtcFssIC0xXSkpCiAgICB5c2hpZnQgPC0gbnVtZXJpYyhucm93KHRtcCkpCiAgICAjIyBTdGFydCBhdCAnYm90dG9tJyByb3cgUmVwZWF0IGZvciByZXN0IG9mIHRoZSByb3dzCiAgICAjIyB1bnRpbCB5b3UgaGl0IHRoZSB0b3AKICAgIGZvciAoaSBpbiAyOm5yb3codG1wKSkgewogICAgICAgICMjIFNoaWZ0IHN1YnNlcXVlbnQgcm93IHVwIGJ5IGVxdWFsIHNwYWNlIHNvIGdhcAogICAgICAgICMjIGJldHdlZW4gdHdvIGVudHJpZXMgaXMgPj0gbWluaW11bQogICAgICAgIG1hdCA8LSBhcy5tYXRyaXgodG1wWyhpIC0gMSk6aSwgLTFdKQogICAgICAgIGQubWluIDwtIG1pbihkaWZmKG1hdCkpCiAgICAgICAgeXNoaWZ0W2ldIDwtIGlmZWxzZShkLm1pbiA8IG1pbi5zcGFjZSwgbWluLnNwYWNlIC0gZC5taW4sCiAgICAgICAgICAgIDApCiAgICB9CgogICAgdG1wIDwtIGNiaW5kKHRtcCwgeXNoaWZ0ID0gY3Vtc3VtKHlzaGlmdCkpCgogICAgc2NhbGUgPC0gMQogICAgdG1wIDwtIG1lbHQodG1wLCBpZCA9IGMoImdyb3VwIiwgInlzaGlmdCIpLCB2YXJpYWJsZS5uYW1lID0gIngiLAogICAgICAgIHZhbHVlLm5hbWUgPSAieSIpCiAgICAjIyBTdG9yZSB0aGVzZSBnYXBzIGluIGEgc2VwYXJhdGUgdmFyaWFibGUgc28gdGhhdCB0aGV5CiAgICAjIyBjYW4gYmUgc2NhbGVkIHlwb3MgPSBhKnlzaGlmdCArIHkKCiAgICB0bXAgPC0gdHJhbnNmb3JtKHRtcCwgeXBvcyA9IHkgKyBzY2FsZSAqIHlzaGlmdCkKICAgIHJldHVybih0bXApCgp9CgpwbG90X3Nsb3BlZ3JhcGggPC0gZnVuY3Rpb24oZGYpIHsKICAgIHlsYWJzIDwtIHN1YnNldChkZiwgeCA9PSBoZWFkKHgsIDEpKSRncm91cAogICAgeXZhbHMgPC0gc3Vic2V0KGRmLCB4ID09IGhlYWQoeCwgMSkpJHlwb3MKICAgIGZvbnRTaXplIDwtIDMKICAgIGdnIDwtIGdncGxvdChkZiwgYWVzKHggPSB4LCB5ID0geXBvcykpICsgZ2VvbV9saW5lKGFlcyhncm91cCA9IGdyb3VwKSwKICAgICAgICBjb2xvdXIgPSAiZ3JleTgwIikgKyBnZW9tX3BvaW50KGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSA4KSArCiAgICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHkpLCBzaXplID0gZm9udFNpemUsIGZhbWlseSA9ICJBbWVyaWNhbiBUeXBld3JpdGVyIikgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIiIsIGJyZWFrcyA9IHl2YWxzLCBsYWJlbHMgPSB5bGFicykKICAgIHJldHVybihnZykKfQoKIyMgUHJlcGFyZSBkYXRhCmRhdGFmIDwtIHR1ZnRlX3NvcnQoc291cmNlX2RmLCB4ID0gInllYXIiLCB5ID0gInZhbHVlIiwgZ3JvdXAgPSAiZ3JvdXAiLAogICAgbWluLnNwYWNlID0gMC4wNSkKCmRhdGFmIDwtIHRyYW5zZm9ybShkYXRhZiwgeCA9IGZhY3Rvcih4LCBsZXZlbHMgPSBjKDUsIDEwLCAxNSwKICAgIDIwKSwgbGFiZWxzID0gYygiNSB5ZWFycyIsICIxMCB5ZWFycyIsICIxNSB5ZWFycyIsICIyMCB5ZWFycyIpKSwKICAgIHkgPSByb3VuZCh5KSkKCiMjIFBsb3QKcGxvdF9zbG9wZWdyYXBoKGRhdGFmKSArIGxhYnModGl0bGUgPSAiRXN0aW1hdGVzIG9mICUgc3Vydml2YWwgcmF0ZXMiKSArCiAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhbWlseSA9ICJBbWVyaWNhbiBUeXBld3JpdGVyIiwKICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQW1lcmljYW4gVHlwZXdyaXRlciIsCiAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpKQpgYGAKIyMjIHNlYXNvbmFsIHBsb3QgCgpgYGB7cn0KcF9sb2FkKGZvcmVjYXN0KQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKIyBTdWJzZXQgZGF0YQpub3R0ZW1fc21hbGwgPC0gd2luZG93KG5vdHRlbSwgc3RhcnQgPSBjKDE5MjAsIDEpLCBlbmQgPSBjKDE5MjUsCiAgICAxMikpICAjIHN1YnNldCBhIHNtYWxsZXIgdGltZXdpbmRvdwoKIyBQbG90Cmdnc2Vhc29ucGxvdChBaXJQYXNzZW5nZXJzKSArIGxhYnModGl0bGUgPSAiU2Vhc29uYWwgcGxvdDogSW50ZXJuYXRpb25hbCBBaXJsaW5lIFBhc3NlbmdlcnMiKQpnZ3NlYXNvbnBsb3Qobm90dGVtX3NtYWxsKSArIGxhYnModGl0bGUgPSAiU2Vhc29uYWwgcGxvdDogQWlyIHRlbXBlcmF0dXJlcyBhdCBOb3R0aW5naGFtIENhc3RsZSIpCmBgYAojIyBncm91cHMKCiMjIyBoaWVyYXJjaGljYWwgZGVuZHJvZ3JhbSAKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcyhnZ2RlbmRybykKcF9sb2FkKGdnZGVuZHJvKQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCmhjIDwtIGhjbHVzdChkaXN0KFVTQXJyZXN0cyksIG1ldGhvZCA9ICJhdmVyYWdlIikgICMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKCmdnZGVuZHJvZ3JhbShoYywgcm90YXRlID0gVFJVRSwgc2l6ZSA9IDIpCmBgYAojIyBDbHVzdGVycyAKCmBgYHtyfQojIGxvYWQvcmVsb2FkIGxpYnJhcmllcyBhcyBuZWVkZWQKcF9sb2FkKGdnYWx0KQpwX2xvYWQoZ2dmb3J0aWZ5KQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQoKIyB3ZSdsbCB1c2UgdGhlIElyaXMgZGF0YXNldAojIGZpbHRlciBvdXQgdGhlIFNwZWNpZXMgY29sdW1uCmRhdGFmIDwtIGlyaXMgJT4lIGRwbHlyOjpzZWxlY3QoLVNwZWNpZXMpCiMgY29tcHV0ZSB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMKcGNhX21vZCA8LSBwcmNvbXAoZGF0YWYpCiMgY29udmVydCB0byBkYXRhZnJhbWUgJiBhZGQgYmFjayB0aGUgU3BlY2llcyBjb2x1bW4KZGZfcGMgPC0gZGF0YS5mcmFtZShwY2FfbW9kJHgsIFNwZWNpZXM9aXJpcyRTcGVjaWVzKQojIGNyZWF0ZSB0aGUgc3Vic2V0dGVkIGRhdGFmcmFtZXMgdG8gYmUgZW5jaXJjbGVkIGluIHRoZSBwbG90CmRmX3BjX3ZpciA8LSBkZl9wYyAlPiUgZHBseXI6OmZpbHRlcihTcGVjaWVzID09ICJ2aXJnaW5pY2EiKSAjIGRmIGZvciAndmlyZ2luaWNhJwpkZl9wY19zZXQgPC0gZGZfcGMgJT4lIGRwbHlyOjpmaWx0ZXIoU3BlY2llcyA9PSAic2V0b3NhIikgICMgZGYgZm9yICdzZXRvc2EnCmRmX3BjX3ZlciA8LSBkZl9wYyAlPiUgZHBseXI6OmZpbHRlcihTcGVjaWVzID09ICJ2ZXJzaWNvbG9yIikgICMgZGYgZm9yICd2ZXJzaWNvbG9yJwogCmdncGxvdChkZl9wYywgYWVzKFBDMSwgUEMyLCBjb2w9U3BlY2llcykpICsgIyBiYXNlIGNhbGwKICAgIGdlb21fcG9pbnQoYWVzKHNoYXBlPVNwZWNpZXMpLCBzaXplPTIpICsgIyBhZGQgcG9pbnRzCiAgICBsYWJzKHRpdGxlPSJJcmlzIENsdXN0ZXJzIiwgCiAgICAgICAgIHN1YnRpdGxlPSJXaXRoIHByaW5jaXBhbCBjb21wb25lbnRzIFBDMSBhbmQgUEMyIGFzIFggYW5kIFkgYXhpcyIsCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogSXJpcyIpICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IDEuMiAqIGMobWluKGRmX3BjJFBDMSksIG1heChkZl9wYyRQQzEpKSwgCiAgICAgICAgICAgICAgICAgICAgeWxpbSA9IDEuMiAqIGMobWluKGRmX3BjJFBDMiksIG1heChkZl9wYyRQQzIpKSkgKyAjIGNoYW5nZSBheGlzIGxpbWl0cyAod2l0aG91dCBkZWxldGluZyBwb2ludHMpCiAgICBnZW9tX2VuY2lyY2xlKGRhdGEgPSBkZl9wY192aXIsIGFlcyh4PVBDMSwgeT1QQzIpKSArICAgIyBkcmF3IGNpcmNsZXMKICAgIGdlb21fZW5jaXJjbGUoZGF0YSA9IGRmX3BjX3NldCwgYWVzKHg9UEMxLCB5PVBDMikpICsgCiAgICBnZW9tX2VuY2lyY2xlKGRhdGEgPSBkZl9wY192ZXIsIGFlcyh4PVBDMSwgeT1QQzIpKQoKYGBgCgojIyBJbnRlcmFjdGl2aXR5IApgYGB7cn0KbGlicmFyeShwbG90bHkpCmRhdGEgPC0gZGF0YS5mcmFtZShjb25kID0gcmVwKGMoImNvbmRpdGlvbl8xIiwgImNvbmRpdGlvbl8yIiksCiAgICBlYWNoID0gMTApLCBteV94ID0gMToxMDAgKyBybm9ybSgxMDAsIHNkID0gOSksIG15X3kgPSAxOjEwMCArCiAgICBybm9ybSgxMDAsIHNkID0gMTYpKQoKbXlfZ3JhcGggPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gbXlfeCwgeSA9IG15X3kpKSArIGdlb21fcG9pbnQoc2hhcGUgPSAxKQoKIyBMZXQncyBtYWtlIGl0IGludGVyYWN0aXZlIHVzaW5nIHRoZSBnZ3Bsb3RseSBmdW5jdGlvbiAhCnAgPC0gZ2dwbG90bHkobXlfZ3JhcGgpCnAKYGBgCiMjIGFuaW1hdGUgCklOQ09NUExFVEUgQ0hFQ0sgTEVTU09OIDYgSUYgWU9VIFdBTlQgCmBgYHtyfQpwX2xvYWQoZ2FwbWluZGVyKQpkYXRhIDwtIGdhcG1pbmRlcgoKbXlfZ3JhcGggPC0gZGF0YSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsIGNvbCA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgpICsgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICAgIGd1aWRlcyhzaXplID0gIm5vbmUiKSArIGxhYnMoeCA9ICJHRFAgcGVyIENhcGl0YSIsIHkgPSAiTGlmZSBFeHBlY3RhbmN5IiwKICAgIGNvbCA9ICIiKQoKcF9sb2FkKGdnYW5pbWF0ZSkKCnAgPC0gbXlfZ3JhcGggKyB0cmFuc2l0aW9uX3RpbWUoeWVhcikKYW5pbWF0ZShwLCB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDUwLCByZW5kZXJlciA9IGZmbXBlZ19yZW5kZXJlcihmb3JtYXQgPSAid2VibSIpKQpgYGAKCmBgYHtyfQpwIDwtIG15X2dyYXBoICsgdHJhbnNpdGlvbl90aW1lKHllYXIpICsgbGFicyh0aXRsZSA9ICJZZWFyOiB7ZnJhbWVfdGltZX0iKQoKYW5pbWF0ZShwLCB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDUwLCByZW5kZXJlciA9IGZmbXBlZ19yZW5kZXJlcihmb3JtYXQgPSAid2VibSIpKQpgYGAKCmBgYHtyfQpwIDwtIG15X2dyYXBoICsgZ2VvbV90ZXh0KGFlcyh4ID0gbWluKGdkcFBlcmNhcCksIHkgPSBtaW4obGlmZUV4cCksCiAgICBsYWJlbCA9IGFzLmZhY3Rvcih5ZWFyKSksIGhqdXN0ID0gLTIsIHZqdXN0ID0gLTAuMiwgYWxwaGEgPSAwLjIsCiAgICBjb2wgPSAiZ3JheSIsIHNpemUgPSAyMCkgKyB0cmFuc2l0aW9uX3N0YXRlcyhhcy5mYWN0b3IoeWVhciksCiAgICBzdGF0ZV9sZW5ndGggPSAwKQoKYW5pbWF0ZShwLCB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDUwLCByZW5kZXJlciA9IGZmbXBlZ19yZW5kZXJlcihmb3JtYXQgPSAid2VibSIpKQpgYGAKCiMgZGF0YSBjbGVhbmluZyAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKcF9sb2FkKGphbml0b3IpCgptb21hIDwtIHJlYWRfY3N2KCJkYXRhX2FydHdvcmtzLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoQmVnaW5EYXRlID0gY29sX251bWJlcigpLAogICAgRW5kRGF0ZSA9IGNvbF9udW1iZXIoKSwgYExlbmd0aCAoY20pYCA9IGNvbF9udW1iZXIoKSwgYENpcmN1bWZlcmVuY2UgKGNtKWAgPSBjb2xfbnVtYmVyKCksCiAgICBgRHVyYXRpb24gKHNlYy4pYCA9IGNvbF9udW1iZXIoKSwgYERpYW1ldGVyIChjbSlgID0gY29sX251bWJlcigpKSkgJT4lCiAgICBjbGVhbl9uYW1lcygpCgpwcm9ibGVtcyhtb21hKQpgYGAKCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQptb21hIDwtIG1vbWEgJT4lCiAgICBtdXRhdGUoZ2VuZGVyID0gc3RyX3JlcGxhY2VfYWxsKGdlbmRlciwgZml4ZWQoIihmZW1hbGUpIiwKICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpLCAiRiIpLCBnZW5kZXIgPSBzdHJfcmVwbGFjZV9hbGwoZ2VuZGVyLAogICAgICAgIGZpeGVkKCIobWFsZSkiLCBpZ25vcmVfY2FzZSA9IFRSVUUpLCAiTSIpLCBudW1fYXJ0aXN0cyA9IHN0cl9jb3VudChnZW5kZXIsCiAgICAgICAgIls6YWxwaGE6XSIpLCBudW1fYXJ0aXN0cyA9IG5hX2lmKG51bV9hcnRpc3RzLCAwKSwgbl9mZW1hbGVfYXJ0aXN0cyA9IHN0cl9jb3VudChnZW5kZXIsCiAgICAgICAgIkYiKSwgbl9tYWxlX2FydGlzdHMgPSBzdHJfY291bnQoZ2VuZGVyLCAiTSIpLCBhcnRpc3RfZ2VuZGVyID0gY2FzZV93aGVuKG51bV9hcnRpc3RzID09CiAgICAgICAgMSAmIG5fZmVtYWxlX2FydGlzdHMgPT0gMSB+ICJGZW1hbGUiLCBudW1fYXJ0aXN0cyA9PQogICAgICAgIDEgJiBuX21hbGVfYXJ0aXN0cyA9PSAxIH4gIk1hbGUiKSkKYGBgCgpXaGF0IGRpZmZlcmVudCBraW5kcyBvZiBhcnQgY2xhc3NpZmljYXRpb25zIGFyZSBhdmFpbGFibGU/CmBgYHtyfQptb21hICU+JQogICAgZGlzdGluY3QoY2xhc3NpZmljYXRpb24pICU+JQogICAgcHJpbnQobiA9IEluZikKCmBgYApmaWx0ZXIgb24gcGFpbnRpbmdzIAoKYGBge3J9CmxpYnJhcnkodGlkeXIpCm1vbWEgPC0gbW9tYSAlPiUKICAgIGZpbHRlcihjbGFzc2lmaWNhdGlvbiA9PSAiUGFpbnRpbmciKSAlPiUKICAgIGRyb3BfbmEoaGVpZ2h0X2NtLCB3aWR0aF9jbSkgJT4lCiAgICBmaWx0ZXIoaGVpZ2h0X2NtID4gMCAmIHdpZHRoX2NtID4gMCkKYGBgCgpzZWxlY3Qgc29tZSBjb2x1bXNuIApgYGB7cn0KbW9tYSA8LSBtb21hICU+JQogICAgc2VsZWN0KHRpdGxlLCBjb250YWlucygiYXJ0aXN0IiksIGNvbnRhaW5zKCJ5ZWFyIiksIGNvbnRhaW5zKCJfY20iKSwKICAgICAgICAgICBjbGFzc2lmaWNhdGlvbiwgZGVwYXJ0bWVudCkKYGBgCmBgYHtyfQp3cml0ZV9jc3YobW9tYSwgImFydHdvcmtzLWNsZWFuZWQuY3N2IikKYGBgCgoKCiMjIHJlYWQgZGF0YQoKYGBge3J9CnBfbG9hZChoZXJlKQpwX2xvYWQocmVhZHIpCnBfbG9hZChkcGx5cikKbW9tYSA8LSByZWFkX2NzdigiYXJ0d29ya3MtY2xlYW5lZC5jc3YiKQpgYGAKCnNvbWUgZ2VuZXJhbCBpbmZvIGxpa2UgdGhlIG51bWJlciAKYGBge3J9CmdsaW1wc2UobW9tYSkKbl9hcnRpc3RzIDwtIG1vbWEgJT4lIGRpc3RpbmN0KGFydGlzdCkgJT4lIHRhbGx5KCkgJT4lIHB1bGwoKSAjIHRhbGx5IGFuZCBwdWxsIGdldCB1cyB0aGUgY291bnQgb2YgdGhlIGRpc3RpbmN0IGFydGlzdHMgCm1vc3RfcGFpbnQgPC0gbW9tYSAlPiUgZHBseXI6OmNvdW50KGFydGlzdCwgc29ydCA9IFRSVUUpCnBhaW5pdG5nX2dlbmRlciA8LSBtb21hICU+JSBkcGx5cjo6Y291bnQoYXJ0aXN0X2dlbmRlcikKYXJ0aXN0X2dlbmRlciA8LSBtb21hICU+JSAgZHBseXI6OmNvdW50KGFydGlzdF9nZW5kZXIsIGFydGlzdCwgc29ydCA9IFRSVUUpCmZlbWFsZV9hcnRpc3RzIDwtIGFydGlzdF9nZW5kZXIgJT4lIGZpbHRlcihhcnRpc3RfZ2VuZGVyID09ICJGZW1hbGUiKQphcnRpc3RfZ2VuZGVyICU+JSB0b3BfbigyKQphcnRpc3RfZ2VuZGVyICU+JSBncm91cF9ieShhcnRpc3RfZ2VuZGVyKSAlPiUgdG9wX24oMSkKYXJ0aXN0X2dlbmRlciAlPiUgZHBseXI6OmNvdW50KGFydGlzdF9nZW5kZXIpCmBgYAojIyB2aXN1YWxpemUgCmBgYHtyfQptb21hX2RpbSA8LSBtb21hICU+JQogICAgZmlsdGVyKGhlaWdodF9jbSA8IDYwMCwgd2lkdGhfY20gPCA3NjApICU+JQogICAgbXV0YXRlKGh3X3JhdGlvID0gaGVpZ2h0X2NtL3dpZHRoX2NtLCBod19jYXQgPSBjYXNlX3doZW4oaHdfcmF0aW8gPgogICAgICAgIDEgfiAidGFsbGVyIHRoYW4gd2lkZSIsIGh3X3JhdGlvIDwgMSB+ICJ3aWRlciB0aGFuIHRhbGwiLAogICAgICAgIGh3X3JhdGlvID09IDEgfiAicGVyZmVjdCBzcXVhcmUiKSkKCmxpYnJhcnkoZ2d0aGVtZXMpICAjIHRvIGxvYWQgdGhlIGZpdmV0aGlydHllaWdodCB0aGVtZQoKZ2dwbG90KG1vbWFfZGltLCBhZXMoeCA9IHdpZHRoX2NtLCB5ID0gaGVpZ2h0X2NtLCBjb2xvdXIgPSBod19jYXQpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArIGdndGl0bGUoIk1vTUEgUGFpbnRpbmdzLCBUYWxsIGFuZCBXaWRlIikgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gIiIsIHZhbHVlcyA9IGMoImdyYXk1MCIsICIjRkY5OTAwIiwKICAgICAgICAiI0IxNENGMCIpKSArIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dCgpKSArCiAgICBsYWJzKHggPSAiV2lkdGgiLCB5ID0gIkhlaWdodCIpCgpgYGAKCmBgYHtyfQpnZ3Bsb3QobW9tYV9kaW0sIGFlcyh4ID0gd2lkdGhfY20sIHkgPSBoZWlnaHRfY20sIGNvbG91ciA9IGh3X2NhdCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgZ2d0aXRsZSgiTW9NQSBQYWludGluZ3MsIFRhbGwgYW5kIFdpZGUiKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiIiwgdmFsdWVzID0gYygiZ3JheTUwIiwgIiNlZTU4NjMiLAogICAgICAgICIjNjk5OWNkIikpICsgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KCkpICsKICAgIGxhYnMoeCA9ICJXaWR0aCIsIHkgPSAiSGVpZ2h0IikgKyBhbm5vdGF0ZSh4ID0gMjAwLCB5ID0gMzgwLAogICAgZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiVGFsbGVyIHRoYW5cbldpZGUiLCBjb2xvciA9ICIjZWU1ODYzIiwKICAgIHNpemUgPSA1LCBoanVzdCA9IDEsIGZvbnRmYWNlID0gMikgKyBhbm5vdGF0ZSh4ID0gMzc1LCB5ID0gMTAwLAogICAgZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiV2lkZXIgdGhhblxuVGFsbCIsIGNvbG9yID0gIiM2OTk5Y2QiLAogICAgc2l6ZSA9IDUsIGhqdXN0ID0gMCwgZm9udGZhY2UgPSAyKQpgYGAKCgoKCgoK